<?xml version="1.0" encoding="UTF-8"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"><channel><title>Sergio del Amo Blog</title><link>https://sergiodelamo.com</link><description>Personal blog of Sergio del Amo</description><pubDate>Mon, 06 Apr 2026 15:36:20 GMT</pubDate><item><title>Agents Skills</title><link>https://sergiodelamo.com/blog/2026-04-06-agents-skills.html</link><description><![CDATA[<blockquote><p>Agent Skills are folders of instructions, scripts, and resources that agents can discover and use to do things more accurately and efficiently.</p></blockquote><ul><li><a href="https://agentskills.io/home">Agent Skills</a></li><li><a href="https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview#skill-structure">Claude Skill Structure</a></li><li><a href="https://developers.openai.com/codex/skills">OpenAI Agent Skills</a></li><li><a href="https://geminicli.com/docs/cli/skills/">Gemini CLI Skills</a></li><li><a href="https://junie.jetbrains.com/docs/agent-skills.html">Junie Agent Skills</a></li></ul>]]></description><guid>2026-04-06-agents-skills</guid><pubDate>Mon, 06 Apr 2026 09:05:40 GMT</pubDate></item><item><title>The 8 Stages of Dev Evolution To AI</title><link>https://sergiodelamo.com/blog/the-8-stages-of-dev-evolution-to-ai.html</link><description><![CDATA[<p><a href="https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04">What stage are you in your AI-assisted coding journey?</a></p><p><img src="https://images.sergiodelamo.com/ai-ide-journey.jpg" alt="The 8 Stages of Dev Evolution To AI" /></p><blockquote><p><strong>Stage 1</strong>: Zero or Near-Zero AI: maybe code completions, sometimes ask Chat questions<br /><strong>Stage 2</strong>: Coding agent in IDE, permissions turned on. A narrow coding agent in a sidebar asks your permission to run tools.<br /><strong>Stage 3</strong>: Agent in IDE, YOLO mode: Trust goes up. You turn off permissions, agent gets wider.<br /><strong>Stage 4</strong>: In IDE, wide agent: Your agent gradually grows to fill the screen. Code is just for diffs.<br /><strong>Stage 5</strong>: CLI, single agent. YOLO. Diffs scroll by. You may or may not look at them.<br /><strong>Stage 6</strong>: CLI, multi-agent, YOLO. You regularly use 3 to 5 parallel instances. You are very fast.<br /><strong>Stage 7</strong>: 10+ agents, hand-managed. You are starting to push the limits of hand-management.<br /><strong>Stage 8</strong>: Building your own orchestrator. You are on the frontier, automating your workflow.</p></blockquote><p><a href="https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04">Go to the linked site</a></p>]]></description><guid>the-8-stages-of-dev-evolution-to-ai</guid><pubDate>Wed, 01 Apr 2026 11:38:12 GMT</pubDate></item><item><title>Build an MCP Server with Java at Devoxx Belgium</title><link>https://sergiodelamo.com/blog/2026-01-15-video-of-my-devoxx-talk-about-mcp.html</link><description><![CDATA[<p>Video of my Devoxx deep dive session about how to build an MCP Server with Java.</p><p><a href="https://www.youtube.com/watch?v=Ne9CGoHwACY">Go to the linked site</a></p>]]></description><guid>2026-01-15-video-of-my-devoxx-talk-about-mcp</guid><pubDate>Thu, 15 Jan 2026 09:29:44 GMT</pubDate></item><item><title>Build an MCP server with Java at Madrid JUG</title><link>https://sergiodelamo.com/blog/2026-01-13-madrid-jug-java-mcp.html</link><description><![CDATA[<p>I delivered a talk in Spanish about how to build an MCP server using Java frameworks like Micronaut, Spring Boot, and Quarkus at Madrid JUG.</p><p>I updated the MCP demos in my <a href="https://github.com/sdelamo/devoxxbe-2025-mcp-server">GitHub Repository</a>, and the <a href="https://www.youtube.com/watch?v=93fbmBLhJB8">video</a> is available on the Madrid JUG YouTube channel.</p><p><img src="https://images.sergiodelamo.com/2026-01-13-madrid-jug-mcp-server.jpg" alt="Madrid JUG Java MCP Server Meetup" /></p><p><a href="https://secure.meetupstatic.com/photos/event/8/0/1/7/highres_532052791.webp?w=1080"><img src="https://images.sergiodelamo.com/2026-01-13-madrid-jug-meetup.png" alt="" /></a></p><p><a href="https://www.youtube.com/watch?v=93fbmBLhJB8">Go to the linked site</a></p>]]></description><guid>2026-01-13-madrid-jug-java-mcp</guid><pubDate>Wed, 14 Jan 2026 19:05:40 GMT</pubDate></item><item><title>StringTemplate</title><link>https://sergiodelamo.com/blog/2026-01-09-string-template.html</link><description><![CDATA[<p>While reading <a href="https://www.manning.com/books/spring-ai-in-action">Spring AI in Action</a>, I discovered <a href="https://www.stringtemplate.org">StringTemplate</a>.</p><blockquote><p>StringTemplate is a Java template engine for generating source code, web pages, emails, or any other formatted text output.StringTemplate is particularly good at code generators, multiple site skins, and internationalization / localization. StringTemplate also powers ANTLR.</p></blockquote><ul><li><a href="https://es.wikipedia.org/wiki/Licencia_BSD">License BSD</a></li><li><a href="https://www.stringtemplate.org">Website</a></li><li><a href="https://central.sonatype.com/artifact/org.antlr/stringtemplate/">Maven Central Repository</a></li><li><a href="https://github.com/antlr/stringtemplate4">GitHub Repository</a></li><li><a href="https://plugins.jetbrains.com/plugin/8041-stringtemplate-v4">IntelliJ Plugin</a></li></ul><p>I am thinking of adding support for StringTemplate in <a href="https://micronaut-projects.github.io/micronaut-views/latest/guide/">Micronaut Views</a>.</p><pre><code class="language-java">String template = &quot;&quot;&quot;        You are a helpful assistant, answering questions about tabletop games.        If you don't know anything about the game or don't know the answer,        say &quot;I don't know&quot;.                The game is {game}.                The question is: {question}.&quot;&quot;&quot;;String prompt = new ST(template, '{', '}')        .add(&quot;game&quot;, question.gameTitle())        .add(&quot;question&quot;, question.question())        .render();</code></pre><p><a href="https://www.stringtemplate.org">Go to the linked site</a></p>]]></description><guid>2026-01-09-string-template</guid><pubDate>Fri, 09 Jan 2026 08:49:03 GMT</pubDate></item><item><title>Micronaut NullAway and JSPecify</title><link>https://sergiodelamo.com/blog/2025-11-21-null-away.html</link><description><![CDATA[<p>In this blog post, I show you how to configure a Micronaut 4 application to use JSpecify and NullAway to protect you from NPEs (Null Pointer Exceptions).</p><h2><a href="http://github.com/uber/NullAway">NullAway</a>:</h2><blockquote><p>NullAway is a tool to help eliminate NullPointerExceptions (NPEs) in your Java code. To use NullAway, first add @Nullable annotations in your code wherever a field, method parameter, or return value may be null. Given these annotations, NullAway performs a series of type-based, local checks to ensure that any pointer that gets dereferenced in your code cannot be null.</p></blockquote><h2>JSpecify Dependency</h2><p>Since Micronaut 4.10, Micronaut BOM contains the JSpecify dependency. Thus, you can add the following dependency to your project without specifying the version:</p><pre><code class="language-xml">&lt;dependency&gt;   &lt;groupId&gt;org.jspecify&lt;/groupId&gt;   &lt;artifactId&gt;jspecify&lt;/artifactId&gt;&lt;/dependency&gt;</code></pre><h2>NullAway with Gradle</h2><p><a href="https://github.com/sdelamo/micronaut-gradle-nullaway-demo/tree/main">GitHub Repository with a Gradle Micronaut Application which uses <code>NullAway</code></a></p><p>To use <a href="http://github.com/uber/NullAway">NullAway</a> in a Gradle Micronaut Application. You need to add the <a href="https://github.com/tbroyer/gradle-errorprone-plugin">Gradle error prone plugin</a> and such configuration:</p><pre><code class="language-kotlin">plugins {......    id(&quot;net.ltgt.errorprone&quot;) version &quot;4.3.0&quot;}dependencies {......    implementation(&quot;org.jspecify:jspecify&quot;)    errorprone(&quot;com.uber.nullaway:nullaway:0.12.12&quot;)    errorprone(&quot;com.google.errorprone:error_prone_core:2.44.0&quot;)}tasks.withType&lt;JavaCompile&gt;().configureEach {    options.errorprone {        check(&quot;NullAway&quot;, net.ltgt.gradle.errorprone.CheckSeverity.ERROR)        option(&quot;NullAway:AnnotatedPackages&quot;, example.micronaut&quot;)        if (name.lowercase().contains(&quot;test&quot;)) {            disable(&quot;NullAway&quot;)        }    }}</code></pre><ul><li><code>check(&quot;NullAway&quot;, CheckSeverity.ERROR)</code> sets <code>NullAway</code> issues to the error level.</li><li><code>option(&quot;NullAway:AnnotatedPackages&quot;, “example.micronaut”)</code> tells <code>NullAway</code> that source code in packages under the <code>example.micronaut</code> namespace should be checked for null dereferences and proper usage of <code>@Nullable</code> annotations, and that class files in these packages should be assumed to have correct usage of <code>@Nullable</code>.</li><li>Then, it disables <code>NullAway</code> on test code.</li></ul><p><a href="https://github.com/uber/NullAway?tab=readme-ov-file#java-non-android">These instructions are described in the NullAway README.md file</a>.</p><h2>NullAway with Maven</h2><p><a href="https://github.com/sdelamo/micronaut-maven-nullaway-demo/tree/main">GitHub Repository with a Maven Micronaut Application which uses <code>NullAway</code></a></p><pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;         xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&gt;......  &lt;properties&gt;......    &lt;error-prone.version&gt;2.29.2&lt;/error-prone.version&gt;    &lt;nullaway.version&gt;0.12.12&lt;/nullaway.version&gt;  &lt;/properties&gt;......  &lt;dependencies&gt;      &lt;dependency&gt;          &lt;groupId&gt;org.jspecify&lt;/groupId&gt;          &lt;artifactId&gt;jspecify&lt;/artifactId&gt;          &lt;version&gt;1.0.0&lt;/version&gt;      &lt;/dependency&gt;......  &lt;/dependencies&gt;  &lt;build&gt;    &lt;plugins&gt;......      &lt;plugin&gt;        &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;        &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;        &lt;configuration&gt;          &lt;compilerArgs&gt;            &lt;arg&gt;-XDcompilePolicy=simple&lt;/arg&gt;            &lt;arg&gt;--should-stop=ifError=FLOW&lt;/arg&gt;            &lt;arg&gt;-Xplugin:ErrorProne -Xep:NullAway:ERROR -XepOpt:NullAway:AnnotatedPackages=example.micronaut&lt;/arg&gt;            &lt;arg&gt;-Amicronaut.processing.group=mn.maven.jspecify&lt;/arg&gt;            &lt;arg&gt;-Amicronaut.processing.module=mn-maven-jspecify&lt;/arg&gt;          &lt;/compilerArgs&gt;          &lt;annotationProcessorPaths combine.children=&quot;append&quot;&gt;            &lt;path&gt;              &lt;groupId&gt;com.google.errorprone&lt;/groupId&gt;              &lt;artifactId&gt;error_prone_core&lt;/artifactId&gt;              &lt;version&gt;${error-prone.version}&lt;/version&gt;            &lt;/path&gt;            &lt;path&gt;              &lt;groupId&gt;com.uber.nullaway&lt;/groupId&gt;              &lt;artifactId&gt;nullaway&lt;/artifactId&gt;              &lt;version&gt;${nullaway.version}&lt;/version&gt;            &lt;/path&gt;            &lt;!-- Existing Micronaut processors --&gt;......          &lt;/annotationProcessorPaths&gt;        &lt;/configuration&gt;      &lt;/plugin&gt;    &lt;/plugins&gt;  &lt;/build&gt;&lt;/project&gt;</code></pre><p>The above example adds two annotation processors. <a href="https://errorprone.info/docs/flags#maven">Error Prone</a> and <a href="https://github.com/uber/NullAway/wiki/Configuration">NullAway</a>.</p><p>The previous code sample <a href="https://github.com/uber/NullAway/wiki/Configuration">configures <code>NullAway</code></a>:</p><ul><li><code>-XepOpt:NullAway:AnnotatedPackages</code> sets the list of packages that should be considered properly annotated according to the NullAway convention</li><li><code>-Xep:NullAway:ERROR</code> sets the NullAway check severity to ERROR, which should cause compilation to fail when <code>NullAway</code> violations are found.</li></ul><p>As <a href="https://errorprone.info/docs/installation#maven">described in the error prone Maven documentation</a>, I added a <code>.mvn/jvmconfig</code> file with the following content:</p><pre><code>--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</code></pre><p>A failure looks like:</p><pre><code>[INFO] ------------------------------------------------------------------------[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.14.1:compile (default-compile) on project mn-maven-jspecify: Compilation failure[ERROR] /Users/sdelamo/github/sdelamo/micronaut-maven-nullaway-demo/src/main/java/example/micronaut/GreetingController.java:[21,39] [NullAway] dereferenced expression greetingService.greet() is @Nullable[ERROR]     (see http://t.uber.com/nullaway )[ERROR] [ERROR] -&gt; [Help 1][ERROR] </code></pre>]]></description><guid>2025-11-21-null-away</guid><pubDate>Fri, 21 Nov 2025 10:32:42 GMT</pubDate></item><item><title>Micronaut Fundamentals</title><link>https://sergiodelamo.com/blog/2025-10-29-micronaut-fundamentals.html</link><description><![CDATA[<p>During this year, I recorded Micronaut Fundamentals, a video course available for free on <a href="https://mylearn.oracle.com/ou/course/micronaut-fundamentals/151938/">Oracle MyLearn</a> with an OCI Free Learning Subscription.</p><p>It is a 7-hour and 37-minute course covering the following topics:</p><ul><li>Introduction to Micronaut</li><li>Micronaut Documentation</li><li>Creating your first Micronaut application</li><li>Using Micronaut in popular IDEs</li><li>Exploring a Micronaut application</li><li>Micronaut Build Plugins</li><li>Micronaut Test</li><li>Micronaut Core Concepts</li><li>Serialization</li><li>Application Events</li><li>Source Code Generation</li><li>Configuration</li><li>Dependency Injection</li><li>HTTP Client</li><li>HTTP Server</li><li>Filters</li><li>Management Endpoints</li><li>Logging</li><li>Validation</li><li>Error formats</li><li>Error handling</li><li>OpenAPI</li><li>Packaging and distribution</li><li>GraalVM Native Image</li><li>Graal Development Kit for Micronaut</li></ul><p>Announcement in <a href="https://blogs.oracle.com/java/post/micronaut-fundamentals">Oracle Blog</a>.</p><p><a href="https://mylearn.oracle.com/ou/course/micronaut-fundamentals/151938/">Go to the linked site</a></p>]]></description><guid>2025-10-29-micronaut-fundamentals</guid><pubDate>Wed, 29 Oct 2025 13:46:22 GMT</pubDate></item><item><title>jCasbin</title><link>https://sergiodelamo.com/blog/2025-10-23-jcasbin.html</link><description><![CDATA[<blockquote><p>jCasbin is a powerful and efficient open-source access control library for Java projects. It provides support for enforcing authorization based on various access control models.</p></blockquote><p><a href="https://github.com/casbin/jcasbin">Go to the linked site</a></p>]]></description><guid>2025-10-23-jcasbin</guid><pubDate>Thu, 23 Oct 2025 14:13:18 GMT</pubDate></item><item><title>System Rules</title><link>https://sergiodelamo.com/blog/2025-10-21-junit-system-rules.html</link><description><![CDATA[<blockquote><p>A collection of JUnit rules for testing code that uses java.lang.System.</p></blockquote><p><a href="https://stefanbirkner.github.io/system-rules/">Go to the linked site</a></p>]]></description><guid>2025-10-21-junit-system-rules</guid><pubDate>Tue, 21 Oct 2025 07:41:15 GMT</pubDate></item><item><title>Add an MCP Server to VS Code</title><link>https://sergiodelamo.com/blog/2025-10-06-vs-code-mcp-server.html</link><description><![CDATA[<p>To register a MCP Server in VS Code open the command palette with (CMD + Shift + P) and search for &quot;Add MCP&quot;</p><p><img src="https://images.sergiodelamo.com/vs-code-preference-add-mcp-server.png" alt="" /></p><p>Select your MCP Server transport (STDIO or HTTP)</p><p><img src="https://images.sergiodelamo.com/vs-code-mcp-server-transport-selection.png" alt="" /></p><p>If HTTP, enter your MCP Server URL.</p><p><img src="https://images.sergiodelamo.com/vs-code-mcp-server-enter-url.png" alt="" /></p><p>Select whether you want to install the MCP Server globally or for the current workspace only.</p><p><img src="https://images.sergiodelamo.com/vs-code-select-global-or-workspace.png" alt="" /></p><p>Enter your MCP Server in the <code>mcp.json</code> configuration file.</p><p>The following example shows an MCP Server which uses Streamable HTTP transport.</p><pre><code class="language-json">{  &quot;servers&quot;: {    &quot;micronautfun&quot;: {      &quot;url&quot;: &quot;https://micronaut.fun/mcp&quot;,      &quot;type&quot;: &quot;http&quot;    }  },  &quot;inputs&quot;: []}</code></pre><p>You should see your MCP listed as installed.</p><p><img src="https://images.sergiodelamo.com/vs-code-mcp-servers-installed-micronaut-fun.png" alt="" /></p>]]></description><guid>2025-10-06-vs-code-mcp-server</guid><pubDate>Fri, 03 Oct 2025 14:35:18 GMT</pubDate></item><item><title>Add an MCP Server to GitHub Copilot in IntelliJ IDEA</title><link>https://sergiodelamo.com/blog/2025-10-03-intellij-idea-github-copilot-mcp-server.html</link><description><![CDATA[<p>To register a MCP Server in GitHub Copilot for IntelliJ IDEA, change to <code>Agent</code> model, click the <code>Tools</code> icon and click <code>Add More Tools...</code></p><p><img src="https://images.sergiodelamo.com/intellij-idea-copilot-mcp-server.png" alt="" /></p><p>Enter your MCP Server in the <code>mcp.json</code> configuration file. The following example shows an MCP Server packaged as a FAT jar which uses STDIO transport.</p><pre><code class="language-json">{	&quot;servers&quot;: {	  &quot;diskspace&quot;: {		&quot;type&quot;: &quot;stdio&quot;,		&quot;command&quot;: &quot;java&quot;,		&quot;args&quot;: [&quot;-jar&quot;, &quot;/Users/sdelamo/bin/diskspace-0.1-all.jar&quot;]	  }	}}</code></pre>]]></description><guid>2025-10-03-intellij-idea-github-copilot-mcp-server</guid><pubDate>Fri, 03 Oct 2025 14:35:18 GMT</pubDate></item><item><title>Add an MCP Server to Claude Code</title><link>https://sergiodelamo.com/blog/2025-10-03-claude-code-mcp-server.html</link><description><![CDATA[<p>You can connect <a href="https://docs.claude.com/en/docs/claude-code/mcp">Claude Code to tools via MCP</a>. You can use the <code>claude mcp add</code> command to add an MCP server.</p><pre><code>claude mcp add --transport http micronautfun https://micronaut.fun/mcp</code></pre><p>The previous command adds an entry in the <code>mcpServers</code> of the project:</p><pre><code class="language-json">&quot;mcpServers&quot;: {   &quot;micronautfun&quot;: {     &quot;type&quot;: &quot;http&quot;,     &quot;url&quot;: &quot;https://micronaut.fun/mcp&quot;   }},</code></pre><p>These commands modify the <code>vi $HOME/.claude.json</code> configuration file.</p><p>You can list your project MCP servers with the <code>claude mcp list</code> command.</p><p><a href="https://docs.claude.com/en/docs/claude-code/mcp">Go to the linked site</a></p>]]></description><guid>2025-10-03-claude-code-mcp-server</guid><pubDate>Fri, 03 Oct 2025 14:35:18 GMT</pubDate></item><item><title>Add an MCP Server to Claude Desktop</title><link>https://sergiodelamo.com/blog/2025-10-03-claude-desktop-mcp-server.html</link><description><![CDATA[<p>To register a MCP Server in Claude Desktop, click on <code>Settings</code>, click the <code>Developer</code>, and click <code>Edit Config</code></p><p><img src="https://images.sergiodelamo.com/claude-desktop-mcp-server.png" alt="" /></p><p>Enter your MCP Server in the <code>claude_desktop_config.json</code> configuration file. The following example shows an MCP Server packaged as a FAT jar which uses STDIO transport.</p><pre><code class="language-json">{  &quot;mcpServers&quot;: {    &quot;diskspace&quot;: {      &quot;command&quot;: &quot;java&quot;,      &quot;args&quot;: [&quot;-jar&quot;, &quot;/Users/sdelamo/bin/diskspace-0.1-all.jar&quot;]    }  }}</code></pre>]]></description><guid>2025-10-03-claude-desktop-mcp-server</guid><pubDate>Fri, 03 Oct 2025 14:35:18 GMT</pubDate></item><item><title>Use Claude Code and Docker Destkop</title><link>https://sergiodelamo.com/blog/2025-10-01-use-claudecode-and-docker-desktop.html</link><description><![CDATA[<p>If you see error messages such as <code>API Error: Connection error</code> while using <a href="https://www.claude.com/product/claude-code">Claude Code</a> and <a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a>, <a href="https://github.com/anthropics/claude-code/issues/653#issuecomment-3028733768">toggling off this setting</a> may help you.</p><p><img src="https://images.sergiodelamo.com/docker-desktop-resources-network-turn-off-use-kernel-networking-for-udp.png" alt="" /></p>]]></description><guid>2025-10-01-use-claudecode-and-docker-desktop</guid><pubDate>Wed, 01 Oct 2025 06:33:27 GMT</pubDate></item><item><title>Devoxx Talk - Build an MCP Server with Java</title><link>https://sergiodelamo.com/blog/2025-08-17-devoxx-be-build-an-mcp-server-with-java.html</link><description><![CDATA[<p>I am excited to be talking at Devoxx Belgium, arguably the most important Java conference in Europe, about how to build an MCP Server with Java.</p><p><a href="https://m.devoxx.com/events/dvbe25/talks/24552/build-an-mcp-server-with-java">Go to the linked site</a></p>]]></description><guid>2025-08-17-devoxx-be-build-an-mcp-server-with-java</guid><pubDate>Sun, 17 Aug 2025 07:37:41 GMT</pubDate></item><item><title>Moving my Mastodon account to foojay.social</title><link>https://sergiodelamo.com/blog/2025-08-04.html</link><description><![CDATA[<p>My new handle is <a href="https://foojay.social/@sdelamo">@sdelamo@foojay.social</a>.</p><p>I <a href="https://www.youtube.com/watch?v=XHK1iXdpvg8">followed this video</a> to migrate from <a href="https://jvm.social">jvm.social</a> to<a href="https://foojay.social/@sdelamo">foojay.social</a>.</p>]]></description><guid>2025-08-04</guid><pubDate>Mon, 04 Aug 2025 11:06:41 GMT</pubDate></item><item><title>Micronaut Constraints Validation</title><link>https://sergiodelamo.com/blog/2025-08-03-micronaut-constraints_validation.html</link><description><![CDATA[<p>In this video, I show you how to set up Micronaut Validation to validate the constraints of a Java Record.</p><p><a href="https://www.youtube.com/watch?v=jJCObNiajtI">Go to the linked site</a></p>]]></description><guid>2025-08-03-micronaut-constraints_validation</guid><pubDate>Sun, 03 Aug 2025 15:54:26 GMT</pubDate></item><item><title>Micronaut Development environment</title><link>https://sergiodelamo.com/blog/2025-08-03-micronaut-development_mode.html</link><description><![CDATA[<p>In this video, I show you how to set up a Micronaut Development environment.</p><p><a href="https://www.youtube.com/watch?v=dEJCN9JDv1o">Go to the linked site</a></p>]]></description><guid>2025-08-03-micronaut-development_mode</guid><pubDate>Sun, 03 Aug 2025 14:54:26 GMT</pubDate></item><item><title>Micronaut @EachProperty</title><link>https://sergiodelamo.com/blog/2025-08-04-micronaut-each-property.html</link><description><![CDATA[<p>In this video, I show you how to use the <code>@EachProperty</code> to load configuration into your Micronaut application.</p><p><a href="https://www.youtube.com/watch?v=rP1-70MZ6HI">Go to the linked site</a></p>]]></description><guid>2025-08-04-micronaut-each-property</guid><pubDate>Sun, 03 Aug 2025 14:54:26 GMT</pubDate></item><item><title>OSS-Fuzz</title><link>https://sergiodelamo.com/blog/oss-fuzzing.html</link><description><![CDATA[<blockquote><p>OSS-Fuzz aims to make common open source software more secure and stable by combining modern fuzzing techniques with scalable, distributed execution.</p></blockquote><p>Micronaut is using this great initiative by Google.</p><p><a href="https://google.github.io/oss-fuzz/">Go to the linked site</a></p>]]></description><guid>oss-fuzzing</guid><pubDate>Sat, 02 Aug 2025 07:07:36 GMT</pubDate></item><item><title>Micronaut CLI upgrade via SDKMan</title><link>https://sergiodelamo.com/blog/2025-08-01-youtube-micronaut-cli-upgrade-via-sdkman.html</link><description><![CDATA[<p>In this video, I show how to update to the latest version of the Micronaut CLI with <a href="https://sdkmain.io">SDKMan</a></p><p><a href="https://www.youtube.com/watch?v=5Oa5khpYCJw">Go to the linked site</a></p>]]></description><guid>2025-08-01-youtube-micronaut-cli-upgrade-via-sdkman</guid><pubDate>Fri, 01 Aug 2025 10:23:18 GMT</pubDate></item><item><title>Micronaut Gradle GitHub Actions</title><link>https://sergiodelamo.com/blog/2025-07-31-youtube-micronaut-gradle-github-actions.html</link><description><![CDATA[<p>In this video, I created a Micronaut application built with Gradle. I pushed it to a GitHub repository and I set up a continuous integration server with GitHub Actions</p><p><a href="https://www.youtube.com/watch?v=BlUkX1kqaks">Go to the linked site</a></p>]]></description><guid>2025-07-31-youtube-micronaut-gradle-github-actions</guid><pubDate>Thu, 31 Jul 2025 17:25:53 GMT</pubDate></item><item><title>Add junit-platform-launcher to your Gradle JUnit 5 builds</title><link>https://sergiodelamo.com/blog/upgrading-to-gradle-8-14-junit-platform-launcher.html</link><description><![CDATA[<p>While updating <a href="https://github.com/micronaut-projects">Micronaut repositories</a> to <a href="https://docs.gradle.org/current/userguide/upgrading_version_8.html#manually_declaring_dependencies">Gradle 8.14</a>, I had to add the <code>org.junit.platform:junit-platform-launcher</code> dependency as a <code>testRuntime</code> Gradle Configuration.</p>]]></description><guid>upgrading-to-gradle-8-14-junit-platform-launcher</guid><pubDate>Thu, 31 Jul 2025 17:15:02 GMT</pubDate></item><item><title>Install Ruby in MacOS</title><link>https://sergiodelamo.com/blog/macos-install-ruby.html</link><description><![CDATA[<p>Excellent tutorial to install Ruby on macOS.</p><p><a href="https://mac.install.guide/ruby/">Go to the linked site</a></p>]]></description><guid>macos-install-ruby</guid><pubDate>Tue, 22 Jul 2025 06:58:38 GMT</pubDate></item><item><title>Kuvasz - Monitoring Service built with Micronaut</title><link>https://sergiodelamo.com/blog/kuvasz_monitoring_service.html</link><description><![CDATA[<blockquote><p>Kuvasz is an open-source, self-hosted uptime &amp; SSL monitoring service, designed to help you keep track of your websites and services</p></blockquote><p><a href="https://github.com/kuvasz-uptime/kuvasz">Go to the linked site</a></p>]]></description><guid>kuvasz_monitoring_service</guid><pubDate>Fri, 18 Jul 2025 09:43:04 GMT</pubDate></item><item><title>MCP Resources</title><link>https://sergiodelamo.com/blog/mcp-resources.html</link><description><![CDATA[<p>Collection of links to Model Context Protocol (MCP) resources, including videos, articles, and tools.</p><blockquote><p>The Model Context Protocol (MCP) was <a href="https://www.anthropic.com/news/model-context-protocol">open sourced by Anthropic</a> in November 2024 to provide users and developers with an easy way to extend the capabilities of AI-powered apps by integrating them with data sources and applications.</p></blockquote><h2><a href="https://modelcontextprotocol.io">Model Context Protocol (MCP)</a></h2><h1>Courses</h1><h2>Anthropic Academy</h2><ul><li><a href="https://anthropic.skilljar.com/introduction-to-model-context-protocol">Introduction to Model Context Protocol</a></li></ul><h2>Courses</h2><ul><li><a href="https://www.linkedin.com/learning/model-context-protocol-mcp-hands-on-with-agentic-ai/">Model Context Protocol with Agentic AI</a></li></ul><h2>Videos</h2><p><a href="https://www.youtube.com/watch?v=FLpS7OfD5-s">Why MCP really is a big deal with Tim Berglund</a></p><h2>MCP Java SDK</h2><ul><li><p><a href="https://github.com/modelcontextprotocol/java-sdk">The official Java SDK for Model Context Protocol servers and clients. Maintained in collaboration with Spring AI</a>. I have contributed some code as well.</p></li><li><p><a href="https://github.com/mcp-java">MCP Java Github Organization</a></p></li><li><p><a href="https://mcp-java.github.io">Java MCP Server Configurator Generator</a></p></li></ul><h2>Spring</h2><p><a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html">Spring AI MCP</a></p><p><a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html">MCP Server Spring Boot Starter Documentation</a></p>]]></description><guid>mcp-resources</guid><pubDate>Thu, 17 Jul 2025 16:11:34 GMT</pubDate></item><item><title>Build an MCP Server with Java</title><link>https://sergiodelamo.com/blog/presentation-mcp-server-java.html</link><description><![CDATA[<p>This session shows how to build an <a href="https://modelcontextprotocol.io">MCP (Model Context Protocol)</a> server in Java.</p><p>We first discuss how to build an MCP server with Java without any framework, and then compare what the frameworks(<a href="https://spring.io/projects/spring-boot">Spring Boot</a>, <a href="https://micronaut.io">Micronaut</a>, and <a href="https://quarkus.io">Quarkus</a>) offer.</p><p>We will implement an MCP server with each framework. The MCP server will expose custom tools that can be accessed by MCP clients.</p><p>You will learn how to test your implementation using the <a href="https://modelcontextprotocol.io/docs/tools/inspector">MCP Inspector tool</a> and connect to your MCP server with <a href="https://www.anthropic.com/claude">Claude</a> as a client.</p><h2>Target Audience</h2><p>Developers interested in creating an <a href="https://modelcontextprotocol.io">MCP (Model Context Protocol)</a> server in Java.</p><h2>Elevator Pitch</h2><p>MCP allows you to expose reusable tools and resources to build agentic microservices. Doing this takes us beyond basic LLM chatbots to dynamic, problem-solving systems that deliver real value in real professional settings. In this session, you will learn how to build your first MCP server.</p><h2>🇪🇸Construye un servidor MCP con Java</h2><p>Esta sesión muestra cómo construir un servidor <a href="https://modelcontextprotocol.io">MCP (Model Context Protocol)</a> en Java.</p><p>Primero discutimos cómo construir un servidor MCP con Java sin ningún framework, y luego comparamos lo que ofrecen los frameworks(<a href="https://spring.io/projects/spring-boot">Spring Boot</a>, <a href="https://micronaut.io">Micronaut</a> y <a href="https://quarkus.io">Quarkus</a>).</p><p>Implementaremos un servidor MCP con cada framework. El servidor MCP expondrá herramientas personalizadas a las que se puede acceder mediante clientes MCP.</p><p>Aprenderás cómo probar tu implementación usando la <a href="https://modelcontextprotocol.io/docs/tools/inspector">herramienta MCP Inspector</a> y cómo conectarte a tu servidor MCP con <a href="https://www.anthropic.com/claude">Claude</a> como cliente.</p>]]></description><guid>presentation-mcp-server-java</guid><pubDate>Tue, 15 Jul 2025 07:06:47 GMT</pubDate></item><item><title>Podcast feed validator</title><link>https://sergiodelamo.com/blog/2025-05-21-podcast-feedvalidation.html</link><description><![CDATA[<p>Check the Health of Your Podcast</p><p><a href="https://www.castfeedvalidator.com">Go to the linked site</a></p>]]></description><guid>2025-05-21-podcast-feedvalidation</guid><pubDate>Wed, 21 May 2025 19:48:03 GMT</pubDate></item><item><title>Git commit Hash in Gradle build file</title><link>https://sergiodelamo.com/blog/2025-03-25-gradle-git-commit-hash.html</link><description><![CDATA[<p>The following snippet shows how to get the Git commit hash in a Gradle build file using the Gradle Kotlin DSL.</p><pre><code class="language-kotlin">fun String.execute(): String {    val process = ProcessBuilder(*this.split(&quot; &quot;).toTypedArray())        .directory(project.rootDir)        .redirectErrorStream(true)        .start()    return process.inputStream.bufferedReader().readText().trim()}val commitHash = &quot;git rev-parse --verify HEAD&quot;.execute()</code></pre>]]></description><guid>2025-03-25-gradle-git-commit-hash</guid><pubDate>Wed, 26 Mar 2025 13:20:50 GMT</pubDate></item><item><title>Wither Methods</title><link>https://sergiodelamo.com/blog/wither_methods.html</link><description><![CDATA[<p>Derived Record Creation with Wither Methods</p><p><a href="https://openjdk.org/jeps/468">JEP 468: Derived Record Creation</a></p><blockquote><p>Records are immutable objects, so developers frequently create new records from old records to model new data. Derived creation streamlines code by deriving a new record from an existing record, specifying only the components that are different.</p></blockquote><p><a href="https://www.sonarsource.com/blog/builders-withers-and-records-java-s-path-to-immutability/#the-wither-approach">The Wither approach</a></p><p><a href="https://nipafx.dev/inside-java-newscast-67/">Java Withers - Java Newscast</a></p>]]></description><guid>wither_methods</guid><pubDate>Thu, 06 Mar 2025 10:40:28 GMT</pubDate></item><item><title>Use a Shortcut To Create a New Text File In a Folder On a Mac</title><link>https://sergiodelamo.com/blog/2025-02-04-Use_a_Shortcut_To_Create_a_New_Text_File_In_a_Folder_On_a_Mac.html</link><description><![CDATA[<blockquote><p>There is no command in the Finder that will let you create a new blank file at your current location. But you can create a Shortcut that will let you do this each time easily. The Shortcut needs to use both JavaScript and shell scripting to get the job done.</p></blockquote><p><a href="https://macmost.com/use-a-shortcut-to-create-a-new-text-file-in-a-folder-on-a-mac.html">Go to the linked site</a></p>]]></description><guid>2025-02-04-Use_a_Shortcut_To_Create_a_New_Text_File_In_a_Folder_On_a_Mac</guid><pubDate>Tue, 04 Feb 2025 11:11:03 GMT</pubDate></item><item><title>Enable the local calendar in Fantastical for Mac</title><link>https://sergiodelamo.com/blog/2025-01-28-fantastical-local-calendars.html</link><description><![CDATA[<p>I found a way to enable this thanks to a <a href="https://www.reddit.com/r/FantasticalCalendar/comments/17ar6b4/support_unable_to_access_local_calendars/">Reddit post</a>.</p><blockquote><p>Fantastical for Mac needs and extra step to enable the local calendar. I’ll copy and paste what they told (it’s working for me).</p></blockquote><blockquote><ul><li>Copy the following line: <a href="x-fantastical3://defaults?key=EventKitSyncAll&amp;value=1&amp;type=bool&amp;group=1">x-fantastical3://defaults?key=EventKitSyncAll&amp;value=1&amp;type=bool&amp;group=1</a></li><li>Open your web browser</li><li>Paste the text into the URL field and press enter</li><li>Allow Fantastical to open</li><li>Confirm the alert that appears</li><li>Quit Fantastical completely (Click Fantastical in the menu bar and choose &quot;Quit Fantastical Completely&quot;) then open the app again.</li></ul></blockquote><p><a href="https://www.reddit.com/r/FantasticalCalendar/comments/17ar6b4/support_unable_to_access_local_calendars/">Go to the linked site</a></p>]]></description><guid>2025-01-28-fantastical-local-calendars</guid><pubDate>Tue, 28 Jan 2025 12:22:04 GMT</pubDate></item><item><title>Kamal deployments at Commit Conf 2025</title><link>https://sergiodelamo.com/blog/2025-01-14-commit-conf-kamal-deployment.html</link><description><![CDATA[<p>I will be delivering a talk about <a href="presentation-kamal-deployment-es.html">Kamal deployments</a> at <a href="https://2025.commit-conf.com">Commit Conf 2025</a>, April 4th and 5th 2025.</p><p><a href="https://koliseo.com/commit/commit-conf-2025/agenda/P-qxG9TF3OPiQ9aPNt7hvK?selected=MzuBfHOfdCz3LqxgxYvI">Go to the linked site</a></p>]]></description><guid>2025-01-14-commit-conf-kamal-deployment</guid><pubDate>Tue, 14 Jan 2025 15:19:00 GMT</pubDate></item><item><title>Configure a Gradle Kotlin DSL to resolve Micronaut Snapshots</title><link>https://sergiodelamo.com/blog/micronaut-snapshot-gradle-kotlin-dsl.html</link><description><![CDATA[<p><a href="https://docs.micronaut.io/4.7.10/guide/#usingsnapshots">Micronaut documentation describes how to use snapshots</a>.</p><p>The following snippet, which uses the <a href="https://docs.gradle.org/current/userguide/kotlin_dsl.html">Kotlin DSL</a>, allows you to resolve Micronaut snapshots:</p><pre><code class="language-kotlin">repositories {    maven {        url = uri(&quot;https://s01.oss.sonatype.org/content/repositories/snapshots/&quot;)        mavenContent {            snapshotsOnly()        }    }    mavenCentral {        mavenContent {            releasesOnly()        }    }}</code></pre>]]></description><guid>micronaut-snapshot-gradle-kotlin-dsl</guid><pubDate>Tue, 07 Jan 2025 10:17:26 GMT</pubDate></item><item><title>Linking Manifesto</title><link>https://sergiodelamo.com/blog/2024-12-18-linking-manifesto.html</link><description><![CDATA[<blockquote><p>Ensuring their users can conveniently obtain a link to the currently open or selected resource via a user interface; and providing an application programming interface (API) to obtain or construct a link to that resource (i.e., to get its address and name).</p></blockquote><p><a href="https://linkingmanifesto.org">Go to the linked site</a></p>]]></description><guid>2024-12-18-linking-manifesto</guid><pubDate>Wed, 18 Dec 2024 08:50:27 GMT</pubDate></item><item><title>Revapi, an API analysis and change tracking tool written in Java</title><link>https://sergiodelamo.com/blog/2024-12-14-revapi.html</link><description><![CDATA[<p>I work on open source. Thus, any tool to prevent unintentional breaking changes piques my interest. In Micronaut, we use <a href="https://github.com/siom79/japicmp"><code>japicmp</code></a>.</p><p><a href="https://revapi.org/revapi-site/main/index.html">Go to the linked site</a></p>]]></description><guid>2024-12-14-revapi</guid><pubDate>Sat, 14 Dec 2024 09:20:42 GMT</pubDate></item><item><title>A Java version of simdjson</title><link>https://sergiodelamo.com/blog/2024-12-14-s.htmljson.html</link><description><![CDATA[<blockquote><p>A Java version of simdjson - a JSON parser using SIMD instructions, based on the paper Parsing Gigabytes of JSON per Second by Geoff Langdale and Daniel Lemire.</p></blockquote><p>I have not tried it, but it looks like an interesting open-source library.</p><p><a href="https://github.com/simdjson/simdjson-java">Go to the linked site</a></p>]]></description><guid>2024-12-14-sjson</guid><pubDate>Sat, 14 Dec 2024 09:20:42 GMT</pubDate></item><item><title>VSCode Lab for Micronaut</title><link>https://sergiodelamo.com/blog/2024-12-14-micronaut-vscode-lab.html</link><description><![CDATA[<blockquote><p>In this lab we will introduce you to the Micronaut tooling available within VS Code. This tooling is very feature-rich and makes building and working with Micronaut applications easy.</p></blockquote><p><a href="https://github.com/graalvm/workshops/blob/meetup/vscode-tools-for-micronaut/README.md">Go to the linked site</a></p>]]></description><guid>2024-12-14-micronaut-vscode-lab</guid><pubDate>Sat, 14 Dec 2024 09:20:42 GMT</pubDate></item><item><title>Caido - A lightweight web security auditing toolkit</title><link>https://sergiodelamo.com/blog/2024-12-14-caido.html</link><description><![CDATA[<blockquote><p>Caido aims to help security professionals and enthusiasts audit web applications with efficiency and ease.</p></blockquote><p>I am interested in web security. I discovered this tool by watching a YouTube video about CSRF.</p><p><a href="https://caido.io">Go to the linked site</a></p>]]></description><guid>2024-12-14-caido</guid><pubDate>Sat, 14 Dec 2024 09:20:42 GMT</pubDate></item><item><title>Build a Software as a Service applications with Micronaut and deploy them with GraalVM</title><link>https://sergiodelamo.com/blog/presentation-micronaut-saas.html</link><description><![CDATA[<p>In this talk, you will learn how <a href="https://micronaut.io">Micronaut</a> eases the development of a Software as a Service application (Saas) with the following capabilities:</p><ul><li>Micronaut Data JDBC Multi-Tenancy with Column Discriminator.</li><li><a href="https://www.graalvm.org/latest/reference-manual/native-image/">GraalVM Native Image</a> Deployment.</li><li><a href="https://spec.openapis.org/oas/latest.html">OpenAPI</a> generation.</li><li>OpenID Connect-based single sign-on.</li><li><a href="https://turbo.hotwired.dev">Turbo</a> Integration - single-page web application without writing any JavaScript.</li><li>Multi-Language Front-end.</li></ul><p>The talk is a mix of slides and code samples.You will see an open-source application and how seamlessly Micronaut integrates the above capabilities.</p><p>After this talk, you will understand what the Micronaut framework offers to simplify the development of your next Saas application.</p><h2>Elevator Pitch</h2><p>Build a Multi-Tenancy, OpenAPI, OpenID Connect-based single sign-on, Turbo Integrated application and deploy it as a GraalVM Native Image in no time.</p>]]></description><guid>presentation-micronaut-saas</guid><pubDate>Thu, 28 Nov 2024 07:06:47 GMT</pubDate></item><item><title>Micronaut vs Spring Boot</title><link>https://sergiodelamo.com/blog/presentation-micronaut-vs-spring.html</link><description><![CDATA[<p>In this talk, Micronaut committer Sergio del Amo compares the feature set of Spring Boot and Micronaut. This talk introduces developers familiar with Spring Boot to the similarities and differences between the frameworks.</p><p>In addition, the talk covers the migration of existing applications and the tools available to simplify the migration to Micronaut.</p><p>Also, you will learn how to use Micronaut within a Spring Boot application.</p>]]></description><guid>presentation-micronaut-vs-spring</guid><pubDate>Thu, 28 Nov 2024 07:06:47 GMT</pubDate></item><item><title>Despliegues Docker con Kamal, adios a la complejidad de Kubernetes</title><link>https://sergiodelamo.com/blog/presentation-kamal-deployment-es.html</link><description><![CDATA[<p>¿Quieres hacer despliegues con Docker pero Kubernetes te parece una bestia y un overkill para tu aplicación?. En esta charla descubrirás una alternativa más sencilla pero poderosa - <a href="https://kamal-deploy.org">Kamal</a>.</p><p>Kamal ofrece zero-downtime deploys, rolling restarts, asset bridging, remote builds, accessory service management, y todo lo que necesitas para desplegar y gestionar tu aplicación web en producción con Docker.</p><p>Durante la charla te mostraré como desplegar una aplicación Micronaut programada con Java y compilada como una imagen nativa con GraalVM con Docker y Kamal. Kamal funciona con todo tipo de aplicación web que pueda ser empaquetada en un contenedor. Es decir, si tu lenguaje de programación o framework no coinciden con el mio, esta charla te servirá igual para descubrir Kamal y como usarlo en tu entorno.</p><p>Si asistes a esta charla descubrirás que es Kamal, que te ofrece y como desplegar rápidamente producción con Docker.</p>]]></description><guid>presentation-kamal-deployment-es</guid><pubDate>Sat, 16 Nov 2024 07:06:47 GMT</pubDate></item><item><title>Docker Deployments with Kamal, bye-bye to Kubernetes complexity</title><link>https://sergiodelamo.com/blog/presentation-kamal-deployment.html</link><description><![CDATA[<p>Do you want to do Docker deployments, but Kubernetes looks like a beast and overkill to you? In this talk, you will discover a simpler but powerful alternative - <a href="https://kamal-deploy.org">Kamal</a>.</p><p>Kamal offers zero-downtime deploys, rolling restarts, asset bridging, remote builds, accessory service management, and everything you need to deploy and manage your web application in production with Docker.</p><p>During this talk, I will show you how to deploy a Micronaut application programmed with Java and compiled into a GraalVM native image with Docker and Kamal. Kamal works with any type of web app that can be containerized. Attend the talk, even if your programming language or framework does not match mine, as you will discover Kamal and how to use it with your toolset.</p><p>If you attend this talk, you will discover what Kamal is, what it offers, and how to deploy fast into production with Docker.</p><h2>Elevator Pitch</h2><p>Docker deployments sound good, but for most companies, deploying with Kubernetes is too complex. Kamal offers a seamless deployment experience for any kind of application that can be containerized.</p><p>This presentation will show developers another way to deploy.</p>]]></description><guid>presentation-kamal-deployment</guid><pubDate>Sat, 16 Nov 2024 07:06:47 GMT</pubDate></item><item><title>On November 5th, 2024, I will talk at XtremeJ 2024</title><link>https://sergiodelamo.com/blog/2024-10-24-xtremej.html</link><description><![CDATA[<p>On November 5th 2024, I will be talking at <a href="https://xtremej.dev/2024/">Xtremej.dev</a>.</p><blockquote><p>We are excited to welcome the entire Java community to join us in the coming XtremeJ online conference. We are going to have short sessions (30 minutes each), with speakers from all over the world, online interactive competitions, and experts panels with whome everyone will be able to interact.<img src="https://images.sergiodelamo.com/xtremej.jpeg" alt="XtremeJ 2024" /></p></blockquote><h2>Getting Started with the Micronaut Framework</h2><p>I will be delivering my talk about &quot;Getting Started with the Micronaut Framework&quot;</p><blockquote><p>This session introduces the Micronaut framework and demonstrates how the Framework’s unique compile-time approach enables the development of ultra-lightweight Java applications. Compelling aspects of the Micronaut framework include: + Develop applications with Java, Kotlin, or Apache Groovy + Sub-second startup time + Small processes that can run in as little as 10 MB of JVM heap + No runtime reflection + Dependency injection and AOP + Reflection-free serialization + A database access toolkit that uses ahead-of-time (AoT) compilation to pre-compute queries for repository interfaces. + Cloud-native features. Sergio will also demonstrate how you can generate GraalVM native images of your Micronaut applications to achieve instant startup and ultra-low memory footprint.</p></blockquote><h2>Discount Codes</h2><p>Limited to the first ten passes, they supplied me with two discount codes:</p><ul><li>Use SERGIO40J to get a 40% discount.</li><li>Use SERGIOFREE to get a Free Pass</li></ul><p><a href="https://xtremej.dev/2024/schedule/"><img src="https://images.sergiodelamo.com/2024-xtremej.jpg" alt="" /></a></p><p><a href="https://xtremej.dev/2024/schedule/">Go to the linked site</a></p>]]></description><guid>2024-10-24-xtremej</guid><pubDate>Thu, 24 Oct 2024 06:05:30 GMT</pubDate></item><item><title>JUnit @Issue annotation</title><link>https://sergiodelamo.com/blog/2024-09-08.html</link><description><![CDATA[<p>I was looking to an equivalent to <a href="https://spockframework.org/spock/docs/2.3/extensions.html#_issue">Spock <code>@Issue</code> annotation</a> for JUnit 5, and I found <a href="https://junit-pioneer.org/docs/issue/">JUnit Pioneer</a>.</p><blockquote><p>JUnit Pioneer provides extensions for JUnit 5 and its Jupiter API. It does not limit itself to proven ideas with wide application but is purposely open to experiments.</p></blockquote><p><a href="https://junit-pioneer.org/docs/issue/">Go to the linked site</a></p>]]></description><guid>2024-09-08</guid><pubDate>Sun, 08 Sep 2024 06:14:17 GMT</pubDate></item><item><title>How to run Writebook in port 80 instead of 5555</title><link>https://sergiodelamo.com/blog/2024-09-06-start-writebook-in-port-80-not-555.html</link><description><![CDATA[<p>I have installed <a href="https://once.com/writebook">Writebook</a> in an Oracle Cloud instance.Writebook uses Docker, and it gets installed by default in host port 5555. However, I want to run Writebook in port 80 of the host because it makes it easier to expose it to the Internet from an Oracle Cloud instance.</p><ul><li>Stop Docker</li><li>Edit <code>hostconfig.json</code> for your container hash <code>sudo vi /var/lib/docker/containers/CONTAINERHASH/hostconfig.json</code> and replace <code>&quot;HostPort&quot;:&quot;5555&quot;</code> with replace <code>&quot;HostPort&quot;:&quot;80&quot;</code>.</li><li>Start Docker.</li></ul><p>Courtesy of this great <a href="https://stackoverflow.com/a/38783433">Stackoverflow answer</a>.</p>]]></description><guid>2024-09-06-start-writebook-in-port-80-not-555</guid><pubDate>Fri, 06 Sep 2024 13:06:42 GMT</pubDate></item><item><title>Install Docker on Oracle Cloud Linux 9</title><link>https://sergiodelamo.com/blog/2024-09-06-oracle-cloud-linux-9-enable-docker.html</link><description><![CDATA[<p>I have been trying to install <a href="https://once.com/writebook">Writebook</a> in an Oracle Cloud instance with Oracle Linux 9. The installer could not install Docker.</p><p>To install it manually, I  followed this <a href="https://collabnix.com/how-to-install-docker-on-oracle-linux-a-step-by-step-guide/">step-by-step guide</a></p><h2>Update System Packages</h2><p>Open a terminal and update your system packages as you see below:</p><pre><code>sudo yum update -y</code></pre><h2>Add Docker Repository</h2><p>Add Docker’s official repository to your system’s yum sources list as you see in the image below:</p><pre><code>sudo yum install -y yum-utilssudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo</code></pre><h2>Install Docker Engine</h2><p>Install Docker Engine, CLI, and contained:</p><pre><code>sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.reposudo yum install docker-ce docker-ce-cli containerd.io</code></pre><h2>Start Docker Service</h2><p>Enable and start the Docker service:</p><pre><code>sudo systemctl enable dockersudo systemctl start docker</code></pre>]]></description><guid>2024-09-06-oracle-cloud-linux-9-enable-docker</guid><pubDate>Fri, 06 Sep 2024 12:36:28 GMT</pubDate></item><item><title>Disable Testcontainers JUnit 5 tests without Docker</title><link>https://sergiodelamo.com/blog/2024-08-19-testcontainers-docker-disable.html</link><description><![CDATA[<p><a href="https://testcontainers.com">Testcontainers</a> simplifies testing against third-party components such as databases. But sometimes, you want to run your Junit 5 tests and skip those requiring Docker.</p><blockquote><p>To run Testcontainers-based tests, you need a Docker-API compatible container runtime, such as using Testcontainers Cloud or installing Docker locally.</p></blockquote><p>Luckily, <a href="https://java.testcontainers.org/test_framework_integration/junit_5/">Testcontainers Junit5</a> integration allows you to toggle these tests by annotating them with <code>@Testcontainers(disabledWithoutDocker = true)</code>.</p><p>See an <a href="https://github.com/micronaut-projects/micronaut-test/pull/1081/commits/47920a39869d0b8978ff58a792f5a7d70f12baa5">example of a commit in Micronaut Test</a>.</p>]]></description><guid>2024-08-19-testcontainers-docker-disable</guid><pubDate>Mon, 19 Aug 2024 08:17:50 GMT</pubDate></item><item><title>Bash script to run a Gradle flaky test a hundred times</title><link>https://sergiodelamo.com/blog/2024-08-12-gradle-flaky-test-bash-script.html</link><description><![CDATA[<p>Today, I was fighting a flaky test in a Micronaut project. I used this simple bash script to run the test a hundred times.</p><pre><code>#!/bin/bashEXIT_STATUS=0for ((i=1; i&lt;=100; i++))do  ./gradlew :http-client:test --tests &quot;io.micronaut.http.client.aop.NotFoundSpec.test 404 handling with Flowable&quot; --rerun-tasks || EXIT_STATUS=$?  if [ $EXIT_STATUS -ne 0 ]; then    exit $EXIT_STATUS  fidoneexit $EXIT_STATUS</code></pre>]]></description><guid>2024-08-12-gradle-flaky-test-bash-script</guid><pubDate>Mon, 12 Aug 2024 12:08:09 GMT</pubDate></item><item><title>Shortcut to open a Maven Package in OSS Index</title><link>https://sergiodelamo.com/blog/2024-08-08-shortcut-to-open-maven-package-in-oss-index.html</link><description><![CDATA[<p>I wrote a simple shortcut to open a Maven Package in the OSS Index. It prompts you to enter a group ID and artifact ID and opens the browser with the URL.</p><p><img src="https://images.sergiodelamo.com/shortcut-maven-package-in-oss-index.gif" alt="" /></p><p><a href="https://www.icloud.com/shortcuts/e83362d08d6848c095f140c23cd068a3">Get the shortcut here</a></p><p><img src="https://images.sergiodelamo.com/shortcut-maven-package-in-oss-index.png" alt="" /></p>]]></description><guid>2024-08-08-shortcut-to-open-maven-package-in-oss-index</guid><pubDate>Thu, 08 Aug 2024 11:12:21 GMT</pubDate></item><item><title>Sonatype Scan Gradle Plugin</title><link>https://sergiodelamo.com/blog/2024-08-08-sonatype-scan-gradle-plugin.html</link><description><![CDATA[<p>Sonatype offers plugins to check for vulnerabilities in your dependencies.</p><h2>Web Search</h2><p>You can <a href="https://ossindex.sonatype.org">search directly</a> in the web interface for vulnerabilities in your dependencies.</p><p>For example, to search for <a href="https://ossindex.sonatype.org/component/pkg:maven/org.json/json"><code>org.json:json</code></a> type: <code>pkg:maven/org.json/json</code>.</p><h2>Scan Your dependencies</h2><p>Sonatype OSS Index offers <a href="https://sonatype.github.io/ossindex-maven/maven-plugin/">Maven</a> and <a href="https://github.com/sonatype-nexus-community/scan-gradle-plugin/#readme">Gradle</a> Plugins. I  focus next on Gradle.</p><h3>Registration</h3><p>I signed up for <a href="https://ossindex.sonatype.org">Sonatype OSS Index</a>.</p><p>I set the OSS Index username/password as <a href="https://blog.mrhaki.com/2015/10/gradle-goodness-setting-global.html">global properties for all Gradle Builds</a>. I added entries to <code>USER_HOME/.gradle/gradle.properties</code>.</p><pre><code class="language-properties">ossIndexUsername=xxx@email.comossIndexPassword=yyyy</code></pre><h2>Setup Gradle Plugin</h2><p>You can get the <a href="https://plugins.gradle.org/plugin/org.sonatype.gradle.plugins.scan">latest version of <code>org.sonatype.gradle.plugins.scan</code>in the Gradle Plugin Portal</a></p><p>Then, you can apply the plugin:</p><pre><code class="language-kotlin">plugins {...id(&quot;org.sonatype.gradle.plugins.scan&quot;) version &quot;2.8.2&quot;}dependencies {    ....    ..    .}ossIndexAudit {    username = project.properties[&quot;ossIndexUsername&quot;].toString()    password = project.properties[&quot;ossIndexPassword&quot;].toString()}</code></pre><p>If your project has a vulnerable dependency, when execute the gradle task <code>ossIndexAudit</code>  will see something like this:</p><pre><code>&gt; Task :ossIndexAudit FAILED  ________  ___   ___  __   ____  ____________   _  __ / ___/ _ \/ _ | / _ \/ /  / __/ / __/ ___/ _ | / |/ // (_ / , _/ __ |/ // / /__/ _/  _\ \/ /__/ __ |/    /\___/_/|_/_/ |_/____/____/___/ /___/\___/_/ |_/_/|_/  _      _                       _   _ /_)    /_`_  _  _ _/_   _  _   (/  /_`_._  _   _/ _/_)/_/ ._//_// //_|/ /_//_//_' (_X /  ///_'/ //_/_\   _/                _//Gradle Scan version: 2.8.2------------------------------------------------------------------------------------------------------------------------------------------------------Checking vulnerabilities in 52 dependenciesFound vulnerabilities in 1 dependencies[1/1] - pkg:maven/org.json/json@20230618 - 1 vulnerability found!   Vulnerability Title:  [CVE-2023-5072] CWE-770: Allocation of Resources Without Limits or Throttling   ID:  CVE-2023-5072   Description:  Denial of Service  in JSON-Java versions up to and including 20230618. Â A bug in the parser means that an input string of modest size ca...   CVSS Score:  (7.5/10, High)   CVSS Vector:  CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H   CVE:  CVE-2023-5072   Reference:  https://ossindex.sonatype.org/vulnerability/CVE-2023-5072?component-type=maven&amp;component-name=org.json%2Fjson&amp;utm_source=ossindex-client&amp;utm_medium=integration&amp;utm_content=1.8.2Execution failed for task '::ossIndexAudit'.&gt; Vulnerabilities detected, check log output to review them</code></pre><p>The plugin scans the whole dependency tree. It scans not just your project dependencies but the dependencies of those dependencies.</p><p><a href="https://github.com/sonatype-nexus-community/scan-gradle-plugin/#readme">Go to the linked site</a></p>]]></description><guid>2024-08-08-sonatype-scan-gradle-plugin</guid><pubDate>Thu, 08 Aug 2024 10:07:04 GMT</pubDate></item><item><title>My first day at Oracle</title><link>https://sergiodelamo.com/blog/2024-08-03-my-first-day-at-oracle.html</link><description><![CDATA[<p>Last Thursday, August 1st, 2024, I started at Oracle as a Principal Member of Technical Staff. I joined the <a href="https://graal.cloud/gdk/">GDK</a>/Micronaut Team at Oracle Labs.</p><p>I want to thank <a href="https://x.com/graemerocher">Graeme Rocher</a> and <a href="https://x.com/j_kindelsberger">Julia Kindelsberger</a> for the opportunity to join the Oracle Labs GDK/Micronaut Team. This role will allow me to continue contributing to Micronaut. A passion, I have been dedicating myself to in the pasts years.</p><p>The new job is quite a change for me. I had a small business, worked as a contractor - later as CTO - in a Spanish startup, and <a href="https://sergiodelamo.com/blog/2024-07-31-my-last-day-at-oci.html">worked as a contractor for a medium-sized US company</a>. All, with a common denominator, I have been self-employed since 2008.</p><p>People start in big companies, then move on to create their own thing. I am doing my career in reverse order. :-]</p>]]></description><guid>2024-08-03-my-first-day-at-oracle</guid><pubDate>Sat, 03 Aug 2024 20:59:20 GMT</pubDate></item><item><title>My Last day at OCI</title><link>https://sergiodelamo.com/blog/2024-07-31-my-last-day-at-oci.html</link><description><![CDATA[<p>In January 2017, I wrote a blog post titled <a href="https://sergiodelamo.com/blog/my-first-day-at-oci.html">my first day at OCI</a>. Today is my last day at OCI.</p><p>It has been seven and a half years. Thanks to OCI, I started a career in Open-Source, first in <a href="https://grails.org">Grails</a> and later in <a href="https://micronaut.io">Micronaut</a>.</p><p><strong>Remote and flexible</strong>. Many years of remote work. I remember the pandemic. I was at home with two kids - 4 and 1 years old. My wife was the only one going out. She kept working in a hospital, buying food, etc. I kept working during those months thanks to my wife and OCI's flexibility.</p><p><strong>Many Hats</strong>. I have done many things:</p><ul><li><strong>Helping clients with Grails/Micronaut</strong>. For example, I vividly remember helping a company to upgrade a Grails application. The catch was its source code variable names, and comments were in Danish. I don't speak Danish. Thus, it was a split desktop with Google Translate and IntelliJ IDEA. :-]</li><li><strong>Becoming a Technical Writer</strong>. I  became a technical writer. I have written/reviewed most of the <a href="https://guides.grails.org">Grails Guides</a> and <a href="https://guides.micronaut.io/latest/index.html">Micronaut Guides</a>. I am incredibly proud of the Micronaut Guides pipeline. Every tutorial contains a sample project which users can download and run. The code snippets in those tutorials come from a real tested code. We generate applications programmatically. We build and run the tests in every tutorial for every Micronaut release. It is easy to upgrade every tutorial, often one line change, to a new version of Micronaut.</li><li><strong>Public Speaker</strong>. I have been <a href="https://sergiodelamo.com/blog/tag/talk.html">talking about Micronaut to Java user groups and conferences</a> internationally.</li><li><strong>Open-Source contributor</strong>. I have contributed a lot of code to Micronaut. For example, I have contributed to <a href="https://micronaut-projects.github.io/micronaut-security/latest/guide/">Security</a>, <a href="https://micronaut-projects.github.io/micronaut-aws/latest/guide/">AWS</a>, <a href="https://micronaut-projects.github.io/micronaut-views/latest/guide/">Views</a>, <a href="https://micronaut-projects.github.io/micronaut-rss/latest/guide/">RSS</a>, <a href="https://micronaut-projects.github.io/micronaut-problem-json/latest/guide/">Problem+JSON</a>, <a href="https://micronaut-projects.github.io/micronaut-email/latest/guide/">Email</a>, <a href="https://micronaut-projects.github.io/micronaut-micronaut/latest/guide/">Microstream</a>, <a href="https://micronaut-projects.github.io/micronaut-opensearch/latest/guide/">OpenSearch</a>, <a href="https://micronaut-projects.github.io/micronaut-chatbots/latest/guide/">Chatbots</a>, and <a href="https://micronaut-projects.github.io/micronaut-multitenancy/latest/guide/">Multi-tenancy</a>.</li><li><strong>Podcast</strong>. I hosted, edited, and published the <a href="https://micronautpodcast.com">Micronaut podcast</a>.</li><li><strong>Training</strong> I have delivered training events, both online and on-site, for both Grails and Micronaut. I taught about Micronaut AWS Lambda integration, Micronaut Security, etc.</li><li><strong>Release Manager</strong>. I have been responsible for Micronaut releases for two years - from the second quarter of 2022 until the second quarter of 2024. We released 47 patch releases, 17 minor releases, and a major version - <a href="https://micronaut.io/2023/07/14/micronaut-framework-4-0-0-released/">Micronaut 4</a>. We aimed to follow strict <a href="https://micronaut.io/micronaut-roadmap/">semantic versioning</a> to ease user applications' upgrade and try to <a href="https://github.com/micronaut-projects/micronaut-core/discussions/categories/planning">share the roadmap with the community</a>.</li><li><strong>Partners</strong>. I am proud of the work we did with Azul for the <a href="https://micronaut-projects.github.io/micronaut-crac/latest/guide/">Micronaut CRaC integration</a>, with <a href="https://micronaut-projects.github.io/micronaut-microstream/latest/guide/">MicroStream</a> and <a href="https://micronaut-projects.github.io/micronaut-eclipsestore/latest/guide/">EclipseStore</a> or <a href="https://micronaut-projects.github.io/micronaut-aws/latest/guide/#lambda">AWS Lambda</a></li><li><strong>Technical Manager</strong>. I lead a small remote distributed team at Unity Foundation working on Micronaut. Without the work of <a href="https://x.com/tim_yates">Tim Yates</a>, <a href="https://x.com/jeremyg484">Jeremy Grelle</a>, and <a href="https://x.com/RaceTripper">Dean Wette</a>, the previously mentioned releases and collaborations would be not possible.</li><li><strong>Oracle Labs collaboration</strong> The previously mentioned releases were only possible with the extensive work of the Oracle Labs Micronaut Team. I am proud of the collaboration and joint effort of two development teams from different companies. It was proof of open-source collaboration by multiple vendors.</li></ul><p><strong>Fun memories</strong>I am incredibly grateful for the St. Louis trip with my family in December 2022. <a href="http://localhost/sergiodelamo.com/blog/partnert-of-objectcomputing.html">OCI welcomed me as a partner</a>. It was a great trip, and having my family there was lovely.</p><p>To wrap up, I would like to thank OCI, especially <a href="https://x.com/bremehrg">Gina Bremehr</a> and Jeff Scott Brown for supporting me during these years.</p>]]></description><guid>2024-07-31-my-last-day-at-oci</guid><pubDate>Wed, 31 Jul 2024 07:00:51 GMT</pubDate></item><item><title>Lichess Editor</title><link>https://sergiodelamo.com/blog/lichess-editor-fen.html</link><description><![CDATA[<p>FEN</p><p><a href="https://lichess.org/editor">Go to the linked site</a></p>]]></description><guid>lichess-editor-fen</guid><pubDate>Wed, 19 Jun 2024 16:00:51 GMT</pubDate></item><item><title>Stackoverflow active questions by Java Frameworks</title><link>https://sergiodelamo.com/blog/2024-06-java-framework-stackoverflow-questions.html</link><description><![CDATA[<p>How do Micronaut, Quarkus, and Spring Boot compare by the number of <a href="https://stackoverflow.com">Stackoverflow</a> questions?</p><h2>June 2024</h2><table><thead><tr><th align="left">Framework</th><th align="left">Stackoverflow questions</th></tr></thead><tbody><tr><td align="left"><a href="https://stackoverflow.com/questions/tagged/grails">Grails</a></td><td align="left">29877</td></tr><tr><td align="left"><a href="https://stackoverflow.com/questions/tagged/micronaut">Micronaut</a></td><td align="left">1824</td></tr><tr><td align="left"><a href="https://stackoverflow.com/questions/tagged/quarkus">Quarkus</a></td><td align="left">4610</td></tr><tr><td align="left"><a href="https://stackoverflow.com/questions/tagged/spring-boot">Spring Boot</a></td><td align="left">149254</td></tr></tbody></table><script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script><script type="text/javascript">google.charts.load('current', {'packages':['corechart']});google.charts.setOnLoadCallback(drawChart);function drawChart() {  var data = new google.visualization.DataTable();  data.addColumn('string', 'Java Framework');  data.addColumn('number', 'Stackoverflow questions');  data.addRows([    ['Micronaut', 1824],	['Quarkus', 4610],	['Grails', 29877],	['Spring Boot', 149254]   ]);   var options = {'title':'Stackoverflow Questions by Java framework',   'width':400,   'height':300};   var chart = new google.visualization.PieChart(document.getElementById('chart_div'));   chart.draw(data, options);}</script><div id="chart_div"></div><p>The oldest the framework, the more questions they have. But it is undeniable the Spring Boot dominance.</p>]]></description><guid>2024-06-java-framework-stackoverflow-questions</guid><pubDate>Tue, 18 Jun 2024 10:45:42 GMT</pubDate></item><item><title>Snowflake IDs</title><link>https://sergiodelamo.com/blog/2024-06-17-snowflakeids.html</link><description><![CDATA[<p>I have been researching <a href="https://en.wikipedia.org/wiki/Snowflake_ID">Snowflake IDs</a> and <a href="https://www.youtube.com/watch?v=aLYKd7h7vgY">how Snowflake IDs work</a>:</p><blockquote><p>Snowflake IDs, or snowflakes, are a form of unique identifier used in distributed computing. <a href="https://blog.x.com/engineering/en_us/a/2010/announcing-snowflake">The format was created by Twitter</a> and is used for the IDs of tweets.</p></blockquote><blockquote><p>Snowflakes are 64 bits in binary. The first 41 bits are a timestamp, representing milliseconds since the chosen epoch. The next 10 bits represent a machine ID, preventing clashes. Twelve more bits represent a per-machine sequence number, to allow creation of multiple snowflakes in the same millisecond. The final number is generally serialized in decimal.</p></blockquote><p>A key characteristic, as with <a href="https://sergiodelamo.com/blog/ksuid.html">KSUID</a>, is that they are sortable:</p><blockquote><p>Snowflakes are sortable by time, because they are based on the time they were created.[2] Additionally, the time a snowflake was created can be calculated from the snowflake. This can be used to get snowflakes (and their associated objects) that were created before or after a particular date.</p></blockquote><h2>Java Libraries</h2><p>I found several Java implementations:</p><p><a href="https://github.com/phxql/snowflake-id"><code>phxql/snowflake-id</code></a>, which is licensed under <a href="https://www.gnu.org/licenses/lgpl-3.0.en.html">LGPL (GNU Lesser General Public License 3.0)</a>. It seems active.</p><p>I also found this <a href="https://github.com/callicoder/java-snowflake">java implementation</a>, which unfortunately it does not specify a license.</p><p>I found an <a href="https://github.com/apache/marmotta/blob/master/libraries/kiwi/kiwi-triplestore/src/main/java/org/apache/marmotta/kiwi/generator/SnowflakeIDGenerator.java">implementation</a> and <a href="https://github.com/apache/marmotta/blob/master/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/generator/SnowflakeTest.java">test</a> in <a href="https://marmotta.apache.org">Apache Marmotta</a>which is licensed with <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 License</a>.</p><p>It seems <a href="https://discord4j.com">Discord4J</a> contains also a Snowflake ID implementation.</p>]]></description><guid>2024-06-17-snowflakeids</guid><pubDate>Mon, 17 Jun 2024 14:31:21 GMT</pubDate></item><item><title></title><link>https://sergiodelamo.com/blog/crac-opensource-steward.html</link><description><![CDATA[<p><a href="">Go to the linked site</a></p>]]></description><guid>crac-opensource-steward</guid><pubDate>Fri, 14 Jun 2024 08:44:56 GMT</pubDate></item><item><title>What is the future of reactive programming?</title><link>https://sergiodelamo.com/blog/the-future-of-reactive-programming.html</link><description><![CDATA[<p>I recently watched an <a href="https://www.youtube.com/watch?v=9si7gK94gLo&amp;t=1156s">AMA (Ask Me Anything) video</a> between <a href="https://twitter.com/briangoetz9">Brian Goetz</a>, Java Language Architect at Oracle, and <a href="https://nipafx.dev">Nicolai Parlog</a>, Java Developer Advocate at Oracle, where Brian Goetz shared</p><p>For Brian, <a href="https://openjdk.org/projects/loom/">Loom</a> will kill reactive programming, making it a transitional technology.On the one hand, reactive programming allows you to have many asynchronous operations going on, way more than the number of threads you have.On the other hand, you give up:</p><ul><li>Control flow statements.</li><li>The ability to write simple loops.</li><li>The ability to sequentially debug your code.</li><li>The ability to get clear stack traces.</li></ul><p>The reactive programming tradeoff is not worth it.</p><p>This is transcription generated with <a href="https://rogueamoeba.com/support/manuals/audiohijack/?page=transcribe">Audio Hijack's Trascribe block</a>:</p><blockquote><p><strong>What do you see as the future of reactive programming in Java going forward?</strong>Yeah, so this is a great question. I'm going to give a controversial answer, which some people will like and some people will hate.I think Loom is going to kill reactive programming. I think we are going to realize very quickly... that reactive programming was a transitional technology.It was a reaction to a problem that we had, and when that problem is taken away, it will become obvious that this is not the solution, the reactive is not the solution we want,and I'll explain, 'cause I don't wanna just make a provocative statement and then move on to a different topic.The problem with Reactive is it gets you one good thing. It gets you this event-driven model that allows you to decouple computations from threads.So you can have a lot of asynchronous operations going on, way more than the number of threads that you have.But the cost of it is really high. Basically, you have to give up so much. many things that the language already gives you.You give up control flow statements.You give up being able to write simple loops.You give up having, you know, being able to sequentially debug your code.You give up being able to get clear stack traces of, how did I get to this point where, you know, I, where I experienced this error, all of those things come for free if you're programming with straightforward, you know,sequential code, and reactive frameworks take that all away from you.So they give you one good thing but they take away a bunch of good things that we were all used to and if your code doesn't work, good luck debugging it, andwhat Loom does is it takes away a bunch of good things that we were all used to and if your code doesn't work, good luck debugging it.it says, well, the real bug was not the programming model. The bug was that threads were too expensive, and so I could only create 1,000, 5,000, 10,000.I can't create a million, and so the, if we just solve that problem of let's make it cheaper to create lots. threads, then people will write just ordinary straightforward sequential code,and it will, using the control flow techniques they know from the language, and using the debuggers that they've already got in their toolbox, and getting clean stack traceswhen there's an error, and it's going to be great, and I think a lot of people will look at React. and say, &quot;Boy, that was, you know, that was a cost and benefit that were not in line.&quot;</p></blockquote><p><a href="https://www.youtube.com/watch?v=9si7gK94gLo&amp;t=1156s">Go to the linked site</a></p>]]></description><guid>the-future-of-reactive-programming</guid><pubDate>Thu, 13 Jun 2024 09:49:43 GMT</pubDate></item><item><title>Deploy a MkDocs site to GitHub Pages via GitHub Actions</title><link>https://sergiodelamo.com/blog/2024-05-17-mkdocs-to-github-pages-via-github-actions-workflow.html</link><description><![CDATA[<p>The following GitHub Action workflow to build and deploy the site to GitHub Pages <code>gh-pages</code> branch:</p><pre><code class="language-yaml">name: Publishon:  push:    branches: [ &quot;main&quot; ]jobs:  build:    runs-on: ubuntu-latest    steps:    - uses: actions/checkout@v4    - name: Set up Python 3.10      uses: actions/setup-python@v3      with:        python-version: &quot;3.10&quot;    - name: ⚙️ Install dependencies      run: |        python -m pip install --upgrade pip        pip install mkdocs    - name: 🏗️ Build Site      run: mkdocs build    - name: 🚀 Deploy      uses: JamesIves/github-pages-deploy-action@v4      with:        folder: site</code></pre>]]></description><guid>2024-05-17-mkdocs-to-github-pages-via-github-actions-workflow</guid><pubDate>Fri, 17 May 2024 22:27:38 GMT</pubDate></item><item><title>PageSpeed Insights</title><link>https://sergiodelamo.com/blog/2024-05-17.html</link><description><![CDATA[<blockquote><p>PageSpeed Insights (PSI) reports on the user experience of a page on both mobile and desktop devices, and provides suggestions on how that page may be improved.</p></blockquote><p>Being a Google Service, it may soon be <a href="https://killedbygoogle.com">killed by Google</a></p><p><a href="https://pagespeed.web.dev">Go to the linked site</a></p>]]></description><guid>2024-05-17</guid><pubDate>Fri, 17 May 2024 13:56:28 GMT</pubDate></item><item><title>Hosting a static website in GitHub Pages and Hover</title><link>https://sergiodelamo.com/blog/2024-05-17-github-pages-hover.html</link><description><![CDATA[<p>In this blog post, I show you how to host a website in <a href="https://pages.github.com">GitHub Pages</a> and manage the domain with <a href="https://www.hover.com">Hover</a></p><h2>Website</h2><p>I recently joined a local chess association in Guadalajara. They did not have a website, so I offered to create one for them. <a href="https://ajedrezcallejeroguadalajara.com">The website</a> is a static website with HTML and <a href="https://getbootstrap.com">Bootstrap</a>.</p><p>The website is simple, but it conveys the association information, and <a href="https://pagespeed.web.dev/analysis/https-ajedrezcallejeroguadalajara-com/uamhg91xi4?form_factor=desktop">it scores 100% in performance, accessibility, best practices and SEO</a>.</p><h2>GitHub Organization</h2><p>Instead of creating it from my personal account, I created a <a href="https://github.com/ajedrezcallejeroguadalajara/">GitHub Organization</a> dedicated to the association.I <a href="https://docs.github.com/en/organizations/managing-organization-settings/verifying-or-approving-a-domain-for-your-organization">verified the domain at the GitHub Organization level</a>.<strong>You need to verify the domain at the organizational level, not the repository level</strong>.</p><h2>GitHub Pages</h2><p><a href="https://docs.micronaut.io">Micronaut</a> publishes its documentation to GitHub Pages deploying from branch <code>gh-pages</code>. For this website, I used <em>GitHub Actions as the source</em> instead. The website changes seem remarkably faster.</p><h3>GitHub Action Workflow</h3><p>The following GitHub Action workflow deploys the contents of the <code>dist</code> folder.</p><pre><code class="language-yaml">name: Deploy static content to Pageson:  push:    branches: [&quot;main&quot;]permissions:  contents: read  pages: write  id-token: writeconcurrency:  group: &quot;pages&quot;  cancel-in-progress: falsejobs:  deploy:    environment:      name: github-pages      url: ${{ steps.deployment.outputs.page_url }}    runs-on: ubuntu-latest    steps:      - name: Checkout        uses: actions/checkout@v4      - name: Setup Pages        uses: actions/configure-pages@v5      - name: Upload artifact        uses: actions/upload-pages-artifact@v3        with:          path: 'dist'      - name: Deploy to GitHub Pages        id: deployment        uses: actions/deploy-pages@v4</code></pre><h2>Domain</h2><p>I use <a href="https://www.hover.com">Hover</a> as a domain registrar. I use the following <a href="https://docs.github.com/en/pages/getting-started-with-github-pages/securing-your-github-pages-site-with-https#verifying-the-dns-configuration">DNS configuration as pointed out by GitHub Pages documentation</a>.</p><table><thead><tr><th align="left">TYPE</th><th align="left">HOST</th><th align="center">VALUE</th><th align="right">TTL</th></tr></thead><tbody><tr><td align="left">A</td><td align="left">@</td><td align="center">185.199.109.153</td><td align="right">15 Minutes</td></tr><tr><td align="left">A</td><td align="left">@</td><td align="center">185.199.110.153</td><td align="right">15 Minutes</td></tr><tr><td align="left">A</td><td align="left">@</td><td align="center">185.199.111.153</td><td align="right">15 Minutes</td></tr><tr><td align="left">A</td><td align="left">@</td><td align="center">185.199.108.153</td><td align="right">15 Minutes</td></tr><tr><td align="left">AAAA</td><td align="left">@</td><td align="center">2606:50c0:8000::153</td><td align="right">15 Minutes</td></tr><tr><td align="left">AAAA</td><td align="left">@</td><td align="center">2606:50c0:8001::153</td><td align="right">15 Minutes</td></tr><tr><td align="left">AAAA</td><td align="left">@</td><td align="center">2606:50c0:8002::153</td><td align="right">15 Minutes</td></tr><tr><td align="left">AAAA</td><td align="left">@</td><td align="center">2606:50c0:8003::153</td><td align="right">15 Minutes</td></tr><tr><td align="left">TXT</td><td align="left">_github-pages-challenge-ajedrezcallejeroguadalajara</td><td align="center">89676ddaaeb7a6adec23d364020599</td><td align="right">15 Minutes</td></tr><tr><td align="left">CNAME</td><td align="left">www</td><td align="center">ajedrezcallejeroguadalajara.github.io</td><td align="right">15 Minutes</td></tr></tbody></table><h2>Cost</h2><p>The total cost for the association is 18 USD per year - the cost of the domain.</p><h2>Summary</h2><p>I hope this post shows you how easy is to host a website in <a href="https://pages.github.com">GitHub Pages</a> and manage the domain with <a href="https://www.hover.com">Hover</a>.</p><p>If you are in the Guadalajara (Spain) area, I hope to play chess against you soon. You can find the meetup information at <a href="https://ajedrezcallejeroguadalajara.com">ajedrezcallejeroguadalajara.com</a>.</p>]]></description><guid>2024-05-17-github-pages-hover</guid><pubDate>Fri, 17 May 2024 09:12:17 GMT</pubDate></item><item><title>MavenLocal repository only for snapshots with the Gradle Kotlin DSL</title><link>https://sergiodelamo.com/blog/2024-05-09-maven-local-snapshot-only.html</link><description><![CDATA[<p>Sometimes, while developing you need to <a href="https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:case-for-maven-local">define mavenLocal() repository</a> in a Gradle build.  Gradle allows you to constraint the repository only to snapshots.</p><p>It is easy to do it with the Gradle Kotlin DSL.</p><pre><code>repositories {    mavenCentral()    mavenLocal {        mavenContent {            snapshotsOnly()        }    }    maven {        setUrl(&quot;https://s01.oss.sonatype.org/content/repositories/snapshots/&quot;)        mavenContent {            snapshotsOnly()        }    }}</code></pre>]]></description><guid>2024-05-09-maven-local-snapshot-only</guid><pubDate>Thu, 09 May 2024 08:55:11 GMT</pubDate></item><item><title>Video of my Micronaut framework introduction talk at the JDevSummit IL conference</title><link>https://sergiodelamo.com/blog/2024-05-micronaut-introduction-jdevsubmmitil2024.html</link><description><![CDATA[<p>I did a Micronaut framework introduction in the <a href="https://jdevsummitil.com">JDevSummit IL</a> conference.</p><p><a href="https://www.youtube.com/watch?v=AR8AGGbkMcg">Go to the linked site</a></p>]]></description><guid>2024-05-micronaut-introduction-jdevsubmmitil2024</guid><pubDate>Sat, 04 May 2024 16:00:06 GMT</pubDate></item><item><title>Idempotence</title><link>https://sergiodelamo.com/blog/idempotence.html</link><description><![CDATA[<p>An idempotent operation is defined as one which, if performed more than once, results in the same outcome.</p><p><a href="https://spring.academy/courses/building-a-rest-api-with-spring-boot/lessons/implementing-post">Spring Academy Implementing POST</a>:</p><blockquote><p>An idempotent operation is defined as one which, if performed more than once, results in the same outcome. In a REST API, an idempotent operation is one that even if it were to be performed several times, the resulting data on the server would be the same as if it had been performed only once.</p></blockquote>]]></description><guid>idempotence</guid><pubDate>Wed, 03 Apr 2024 09:16:33 GMT</pubDate></item><item><title>Micronaut framework introduction on JDevSummitIL</title><link>https://sergiodelamo.com/blog/2024-03-20-jdevsummitil-getting-started-with-the-micronaut-framework.html</link><description><![CDATA[<p>I will do an online talk about getting started with the Micronaut Framework at <a href="https://jdevsummitil.com">JDevSummitIL</a>.</p><p><a href="https://jdevsummitil.com/speakers/sergio-del-amo/">Go to the linked site</a></p>]]></description><guid>2024-03-20-jdevsummitil-getting-started-with-the-micronaut-framework</guid><pubDate>Tue, 19 Mar 2024 06:25:22 GMT</pubDate></item><item><title>Create a Status Page</title><link>https://sergiodelamo.com/blog/status-pages.html</link><description><![CDATA[<p>I do a lot of open source releases in my work.</p><p>I have a checklist to verify the necessary services are available by visiting the following status pages:</p><ul><li><a href="https://status.gradle.com">Gradle Status</a></li><li><a href="https://status.maven.org">Sonatype</a></li><li><a href="https://www.githubstatus.com">GitHub</a></li></ul><p>At <a href="https://unityfoundation.io">UnityFoundation</a> we use <a href="https://basecamp.com">Basecamp</a>. Yesterday, Basecamp was suffering a DDOS attack and I visited their <a href="https://www.37status.com">status page</a>. It uses <a href="https://www.atlassian.com/software/statuspage">Atlassian Statuspage</a>. Atlassian statuspage has a free plan.</p><blockquote><p>Our free plan gives you access to 100 subscribers, 25 components, two team members, two metrics, email notifications, Slack notifications, and access to REST APIs.</p></blockquote><p>Atlassian statuspage  is a good option if you need to set up a status page for your service.</p>]]></description><guid>status-pages</guid><pubDate>Thu, 25 Jan 2024 09:49:15 GMT</pubDate></item><item><title>Website Monitoring</title><link>https://sergiodelamo.com/blog/website-monitoring.html</link><description><![CDATA[<p>Serveral options to monitor a website.</p><ul><li><a href="https://www.pingdom.com">Pingdom</a></li><li><a href="https://www.statuscake.com">Statuscake</a></li><li><a href="https://pingability.com">Pingability</a></li></ul><p>I have used both Pingdom and Pingability in the past.</p>]]></description><guid>website-monitoring</guid><pubDate>Tue, 09 Jan 2024 20:41:03 GMT</pubDate></item><item><title>WebJars</title><link>https://sergiodelamo.com/blog/webjars.html</link><description><![CDATA[<blockquote><p>WebJars are client-side web libraries (e.g. jQuery &amp; Bootstrap) packaged into JAR (Java Archive) files.</p></blockquote><p>It's a clever project to get started with a Javascript library in a Java project.</p><p><a href="https://www.webjars.org">Go to the linked site</a></p>]]></description><guid>webjars</guid><pubDate>Tue, 09 Jan 2024 14:30:51 GMT</pubDate></item><item><title>English Pluralization Java library</title><link>https://sergiodelamo.com/blog/english-plurals-java-library.html</link><description><![CDATA[<blockquote><p>Evo Inflector implements English pluralization algorithm based on <a href="https://en.wikipedia.org/wiki/Damian_Conway">&quot;Damian Conway's&quot;</a> paper <a href="http://www.csse.monash.edu.au/~damian/papers/HTML/Plurals.html">&quot;An Algorithmic Approach to English Pluralization&quot;</a>.</p></blockquote><pre><code class="language-java">English.plural(&quot;virus&quot;) == &quot;viruses&quot;</code></pre><p><a href="https://github.com/atteo/evo-inflector">Go to the linked site</a></p>]]></description><guid>english-plurals-java-library</guid><pubDate>Mon, 08 Jan 2024 09:23:53 GMT</pubDate></item><item><title>Micronaut Guide Asciidoc of OpenAPI</title><link>https://sergiodelamo.com/blog/2023-12-11-micronaut-guide-openapi-asciidoc.html</link><description><![CDATA[<p>I wrote a Micronaut Tutorial that shows how to generate an OpenAPI Specification of your Micronaut Application at build time and generate it in Asciidoc format.</p><p><a href="https://guides.micronaut.io/latest/micronaut-openapi-adoc.html">Go to the linked site</a></p>]]></description><guid>2023-12-11-micronaut-guide-openapi-asciidoc</guid><pubDate>Tue, 12 Dec 2023 12:45:09 GMT</pubDate></item><item><title>Micronaut Guide Content Negotiation</title><link>https://sergiodelamo.com/blog/2023-12-12-micronaut-guide-content-negoatiation.html</link><description><![CDATA[<p>I wrote a Micronaut tutorial which shows how to respond HTML or JSON depending on the request Accept HTTP Header.</p><p><a href="https://guides.micronaut.io/latest/micronaut-content-negotiation.html">Go to the linked site</a></p>]]></description><guid>2023-12-12-micronaut-guide-content-negoatiation</guid><pubDate>Tue, 12 Dec 2023 12:43:16 GMT</pubDate></item><item><title>Micronaut EclipseStore</title><link>https://sergiodelamo.com/blog/jconworld-2023-micronaut-eclipsestore.html</link><description><![CDATA[<p>Talk at JCON World 2023</p><p><img src="https://images.sergiodelamo.com/F-9cZjrXIAE8R9x.jpeg" alt="" /></p><p><a href="https://jconworld2023.sched.com/event/1VJaP">Go to the linked site</a></p>]]></description><guid>jconworld-2023-micronaut-eclipsestore</guid><pubDate>Wed, 15 Nov 2023 12:34:37 GMT</pubDate></item><item><title>Micronaut CRaC</title><link>https://sergiodelamo.com/blog/jconworld-2023-micronaut-crac.html</link><description><![CDATA[<p>Talk at JCON World 2023</p><p><img src="https://images.sergiodelamo.com/F-6p4gUWEAAazNG.jpeg" alt="" /></p><p><a href="https://jconworld2023.sched.com/event/1VPsP">Go to the linked site</a></p>]]></description><guid>jconworld-2023-micronaut-crac</guid><pubDate>Wed, 15 Nov 2023 12:34:37 GMT</pubDate></item><item><title>Bash Script to run the same test multiple times in a Gradle build</title><link>https://sergiodelamo.com/blog/bash-script-for-flaky-gradle-test.html</link><description><![CDATA[<blockquote><p>A flaky test is a software test that yields both passing and failing results despite zero changes to the code or test</p></blockquote><pre><code class="language-bash">#!/bin/bashEXIT_STATUS=0NUM_RUNS=100SUCCESSFUL_RUNS=0for ((i=1; i&lt;=$NUM_RUNS; i++)); do  ./gradlew :micronaut-gcp-pubsub:test --tests io.micronaut.gcp.pubsub.integration.SubscriberShutdownSpec --rerun || EXIT_STATUS=$?  if [ $EXIT_STATUS -ne 0 ]; then	exit $EXIT_STATUS  fidoneexit $EXIT_STATUS</code></pre>]]></description><guid>bash-script-for-flaky-gradle-test</guid><pubDate>Tue, 17 Oct 2023 16:31:47 GMT</pubDate></item><item><title>Server-side HTML with Thymeleaf and Micronaut Views</title><link>https://sergiodelamo.com/blog/micronaut-guide-thymeleaf.html</link><description><![CDATA[<p>I wrote a guide that shows how to render server-side HTML with Thymeleaf and Micronaut Views.</p><p><a href="https://guides.micronaut.io/latest/micronaut-views-thymeleaf.html">Go to the linked site</a></p>]]></description><guid>micronaut-guide-thymeleaf</guid><pubDate>Thu, 12 Oct 2023 09:11:38 GMT</pubDate></item><item><title>Gradle Java Test Fixtures Plugin</title><link>https://sergiodelamo.com/blog/gradle-java-test-fixtures.html</link><description><![CDATA[<p>Gradle Plugin which automatically create a <code>testFixtures</code> source set, in which you can write your test fixtures.</p><blockquote><p>Test fixtures are commonly used to setup the code under test, or provide utilities aimed at facilitating the tests of a component. Java projects can enable test fixtures support by applying the <code>java-test-fixtures</code> plugin, in addition to the java or java-library plugins.</p></blockquote><blockquote><p>This will automatically create a testFixtures source set, in which you can write your test fixtures. Test fixtures are configured so that:</p></blockquote><blockquote><ul><li>they can see the main source set classes</li></ul></blockquote><blockquote><p>test sources can see the test fixtures classes</p></blockquote><p><a href="https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures">Go to the linked site</a></p>]]></description><guid>gradle-java-test-fixtures</guid><pubDate>Tue, 03 Oct 2023 11:05:33 GMT</pubDate></item><item><title>System Stubs</title><link>https://sergiodelamo.com/blog/system-stubs.html</link><description><![CDATA[<blockquote><p>System Stubs is used to test code which depends on methods in java.lang.System.</p></blockquote><pre><code class="language-java">@ExtendWith(SystemStubsExtension.class)class WithEnvironmentVariables {	@SystemStub	private EnvironmentVariables variables = new EnvironmentVariables(&quot;input&quot;, &quot;foo&quot;);	@Test	void hasAccessToEnvironmentVariables() {		assertThat(System.getenv(&quot;input&quot;)).isEqualTo(&quot;foo&quot;);	}	@Test	void changeEnvironmentVariablesDuringTest() {		variables.set(&quot;input&quot;, &quot;bar&quot;);		assertThat(System.getenv(&quot;input&quot;)).isEqualTo(&quot;bar&quot;);	}}</code></pre><p><a href="https://github.com/webcompere/system-stubs#system-stubs">Go to the linked site</a></p>]]></description><guid>system-stubs</guid><pubDate>Tue, 03 Oct 2023 10:58:16 GMT</pubDate></item><item><title>Micronaut Framework 4.1.3</title><link>https://sergiodelamo.com/blog/micronaut-framework-413-released.html</link><description><![CDATA[<p>Patch release with Core, Oracle Cloud, Serialization, Netty, and SQL updates.</p><p><a href="https://micronaut.io/2023/10/02/micronaut-framework-4-1-3-released/">Go to the linked site</a></p>]]></description><guid>micronaut-framework-413-released</guid><pubDate>Mon, 02 Oct 2023 10:46:22 GMT</pubDate></item><item><title>Generate reflection Metadata for GraalVM Native Image</title><link>https://sergiodelamo.com/blog/micronaut-guide-graalvm-reflection.html</link><description><![CDATA[<p>I wrote a guide about several methods to provide the required metadata for reflection in a Micronaut application distributed as a GraalVM Native executable.</p><p><a href="https://guides.micronaut.io/latest/micronaut-graalvm-reflection.html">Go to the linked site</a></p>]]></description><guid>micronaut-guide-graalvm-reflection</guid><pubDate>Fri, 29 Sep 2023 15:11:38 GMT</pubDate></item><item><title>Method to check if GraalVM JDK Distribution</title><link>https://sergiodelamo.com/blog/2023-09-21-check-if-java-graalvm.html</link><description><![CDATA[<p>Method to check if you are running in <a href="https://www.graalvm.org">GraalVM</a> JDK distribution. I have used it often in Gradle build files to decide whether a <a href="https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:locating_tasks">Gradle task</a> should be enabled.</p><pre><code class="language-java">private static boolean isGraalVMJava() {    return        (System.getProperty(&quot;java.home&quot;) != null &amp;&amp; java.nio.file.Files.exists(java.nio.file.Paths.get(&quot;${System.getProperty(&quot;java.home&quot;)}/lib/graalvm&quot;)))        || Arrays.asList(&quot;jvmci.Compiler&quot;, &quot;java.vendor.version&quot;, &quot;java.vendor&quot;)            .stream()            .anyMatch(propertyName -&gt; {                String value = System.getProperty(propertyName);                return value != null &amp;&amp; value.toLowerCase(Locale.ENGLISH).contains(&quot;graal&quot;);            });}</code></pre>]]></description><guid>2023-09-21-check-if-java-graalvm</guid><pubDate>Thu, 21 Sep 2023 17:08:30 GMT</pubDate></item><item><title>Micronaut framework introduction on the Barranquilla Java Users Group</title><link>https://sergiodelamo.com/blog/2023-07-micronaut-introduction-barranquilla-jug.html</link><description><![CDATA[<p>I did a Micronaut framework introduction in Spanish for the Barranquilla Java Users Group</p><p><a href="https://www.youtube.com/watch?v=RMaFT2BVIHI">Go to the linked site</a></p>]]></description><guid>2023-07-micronaut-introduction-barranquilla-jug</guid><pubDate>Mon, 10 Jul 2023 19:02:43 GMT</pubDate></item><item><title>YAML Configuration with Micronaut Framework 4</title><link>https://sergiodelamo.com/blog/micronaut-framework-4-yaml-configuration.html</link><description><![CDATA[<p>I recorded a video showing the changes for YAML configuration in Micronaut Framework 4.</p><p><a href="https://www.youtube.com/watch?v=MbmQvyJ-tDI">Go to the linked site</a></p>]]></description><guid>micronaut-framework-4-yaml-configuration</guid><pubDate>Sun, 18 Jun 2023 16:04:09 GMT</pubDate></item><item><title>JCON 2023 - How to Deploy in AWS a Micronaut Application which Persists with MicroStream?</title><link>https://sergiodelamo.com/blog/jcon-2023-micronaut-and-microstream-and-aws.html</link><description><![CDATA[<p>Next week, I fly to Cologne, Germany, to present the Microstream integration with the Micronaut Framework and how to go to production with AWS.</p><p><img src="https://images.sergiodelamo.com/sergio-del-amo-how-to-deploy-in-aws-a-micronaut-application-which-persists-with-microstream.jpg" alt="Micronaut Framework and Microstream Java-Native Persistence Engine and AWS deployment" /></p><p><a href="https://jconeurope2023.sched.com/event/1K3ys/how-to-deploy-in-aws-a-micronaut-application-which-persists-with-microstream">Go to the linked site</a></p>]]></description><guid>jcon-2023-micronaut-and-microstream-and-aws</guid><pubDate>Mon, 12 Jun 2023 20:31:27 GMT</pubDate></item><item><title>JCON 2023 - Micronaut Framework and Microstream Java-Native Persistence Engine</title><link>https://sergiodelamo.com/blog/jcon-2023-micronaut-and-microstream.html</link><description><![CDATA[<p>Next week, I fly to Cologne, Germany, to present the Microstream integration with the Micronaut Framework.</p><p><img src="https://images.sergiodelamo.com/sergio-del-amo-micronaut-framework-microstream-ava-native-persistence-engine.jpg" alt="Micronaut Framework and Microstream Java-Native Persistence Engine" /></p><p><a href="https://jconeurope2023.sched.com/event/1K3zq/micronautr-framework-and-microstream-java-native-persistenc">Go to the linked site</a></p>]]></description><guid>jcon-2023-micronaut-and-microstream</guid><pubDate>Mon, 12 Jun 2023 20:31:27 GMT</pubDate></item><item><title>HazeOver — Distraction Dimmer for Mac</title><link>https://sergiodelamo.com/blog/hazeover-focus-macos.html</link><description><![CDATA[<p>Turn distractions down and focus on your current task.</p><p>I discovered this application in episode <a href="https://www.relay.fm/mpu/684">#684 of the MacPower Users podcast</a>.</p><p><a href="https://hazeover.com">Go to the linked site</a></p>]]></description><guid>hazeover-focus-macos</guid><pubDate>Wed, 22 Mar 2023 08:59:22 GMT</pubDate></item><item><title>Restore windows layouts with Moom</title><link>https://sergiodelamo.com/blog/moom-arrange-windows-according-to-snapshot.html</link><description><![CDATA[<p><a href="https://manytricks.com/moom/">Moom</a> has capabilities to <em>Save and restore window layouts</em>.</p><blockquote><p>Set up a collection of windows in the size and locations you wish, then save the layout.  Restore the layout via an assigned hot key or via Moom's menus.</p></blockquote><p>Moom has some AppleScript functionality.</p><pre><code class="language-AppleScript">tell application &quot;Moom&quot; to arrange windows according to snapshot &quot;Snapshot&quot;</code></pre><p>Next is to <a href="https://manytricks.com/blog/?p=5662">use Moom in MacOS Shortcuts</a> and with <a href="https://brettterpstra.com/2020/09/14/using-moom-with-bunch-for-window-management/">Bunch</a>.</p>]]></description><guid>moom-arrange-windows-according-to-snapshot</guid><pubDate>Sun, 12 Mar 2023 07:03:22 GMT</pubDate></item><item><title>Micronaut CRaC</title><link>https://sergiodelamo.com/blog/micronaut-crac.html</link><description><![CDATA[<p>In this talk, Sergio del Amo introduces CRaC (Coordinated Restore at Checkpoint) and its support within the Micronaut Framework.</p><p>I did a similar presentation at<a href="https://www.youtube.com/watch?v=VdVVq4tMGp8">Madrid Java User Group June 2023</a>, and<a href="/blog/jconworld-2023-micronaut-crac.html">JCON World 2023</a></p><p><a href="https://www.azul.com/products/components/crac/">CRaC (Coordinated Restore at Checkpoint)</a> is an open-source project aiming to speed up the startup of Java applications. CRaC is getting much attention in the Java ecosystem because it offers startup gains comparable to native executables generated by GraalVM. However, it offers these startup-up improvements without many native image generation challenges.</p><p>This talk covers:</p><ul><li>Introduction to CRaC (Coordinated Restore at Checkpoint)</li><li>CRaC constraints.</li><li>Micronaut CRaC integration.</li><li>CRaC packaging options for Micronaut applications.</li><li>Micronaut CRaC with Google Cloud Run</li><li>Micronaut CRaC with AWS Lambda SnapStart.</li></ul><p>Attendees will learn CRaC, its benefits and tradeoffs, and how to use it in their applications.</p><h2>Elevator Pitch</h2><p>Do you want to get amazing startup improvements without the GraalVM native image generation headaches? CRaC is the solution you have been looking for. Every Java developer should learn what CRaC is, what problem it solves, which scenarios are the perfect fit for CRaC, and how to start using it today.</p>]]></description><guid>micronaut-crac</guid><pubDate>Fri, 10 Feb 2023 17:29:07 GMT</pubDate></item><item><title>How to deploy in AWS a Micronaut application which persists with MicroStream?</title><link>https://sergiodelamo.com/blog/micronaut-microstream-aws.html</link><description><![CDATA[<p>Similar presentation at <a href="https://www.youtube.com/watch?v=Azy6nSBVw7k">JCON Online 2022</a>.</p><hr /><p>In this talk, Sergio shows how you can deploy a Micronaut application that uses MicroStream for persistence to Amazon Web Services (AWS).</p><p>You will see an example of a persistence layer built with MicroStream in a Micronaut application.</p><p>We will discuss the application architecture. The savings MicroStream persistence offers and the performance metrics we see.</p><p>After this talk, you will understand the steps necessary to deploy your next project to production with the Micronaut framework, Microstream, and AWS.</p>]]></description><guid>micronaut-microstream-aws</guid><pubDate>Tue, 31 Jan 2023 18:36:59 GMT</pubDate></item><item><title>Micronaut Data and CosmosDB</title><link>https://sergiodelamo.com/blog/micronaut-data-cosmos-db.html</link><description><![CDATA[<p>Micronaut Framework is a modern JVM and modular framework. One of its most powerful modules is Micronaut Data. Micronaut Data is a database access toolkit that uses Ahead of Time (AoT) compilation to pre-compute queries for repository interfaces. A thin, lightweight runtime layer executes those queries.</p><p>Since Micronaut Framework 3.8.0, Micronaut Data offers a flavor for [Azure CosmosDB] which supports some of the features Micronaut Data developers love and expect, including:</p><ul><li>Repositories compile-time generated and projection queries</li><li>Attribute converters</li><li>Optimistic locking</li></ul><p>In this talk, Sergio del Amo introduces Micronaut Data Azure CosmosDB integration and shows you how easy it is to get started.</p>]]></description><guid>micronaut-data-cosmos-db</guid><pubDate>Tue, 31 Jan 2023 18:36:59 GMT</pubDate></item><item><title>Shortcut to verify if a Micronaut module's version is available in SonaType</title><link>https://sergiodelamo.com/blog/shortcut-macos-version-in-sonatype.html</link><description><![CDATA[<p>I created this MacOS Shortcut to verify if a Micronaut module's version is available in SonaType.</p><p><img src="https://images.sergiodelamo.com/shortcuts-check-micronaut-module-sonatype.png" alt="Shortcuts check if Micronaut Module is in SonaType" /></p><p>If the version is available, I get the following notification:</p><p><img src="https://images.sergiodelamo.com/shortcuts-check-micronaut-module-sonatype-success.png" alt="Shortcuts check if Micronaut Module is in SonaType" /></p><p><a href="https://www.icloud.com/shortcuts/6b1f812be47d40a49a8432904cf48e7a">iCloud Link</a></p>]]></description><guid>shortcut-macos-version-in-sonatype</guid><pubDate>Fri, 20 Jan 2023 08:53:35 GMT</pubDate></item><item><title>#2 Top GitHub Users By Public Contributions in Spain</title><link>https://sergiodelamo.com/blog/number_2_in_top-github-users-by-public-contributions-in-spain.html</link><description><![CDATA[<p>I am number 2 in <a href="https://github.com/gayanvoice/top-github-users/blob/main/markdown/public_contributions/spain.md">Top GitHub Users By Public Contributions in Spain</a>.</p><p>I don't know what that means, if anything. I am lucky to work with open source. Thus, 99% of my work is in public.</p><p><img src="https://images.sergiodelamo.com/top-users-by-public-contribution-spain.png" alt="" /></p>]]></description><guid>number_2_in_top-github-users-by-public-contributions-in-spain</guid><pubDate>Wed, 11 Jan 2023 06:23:25 GMT</pubDate></item><item><title>Serverless monolith</title><link>https://sergiodelamo.com/blog/serverless-monoltih.html</link><description><![CDATA[<p>In this talk, Sergio del Amo discusses a serverless monolith architecture. An architecture composed of a big serverless function and some small functions for tasks such as schedule jobs, async notifications, and WebSockets.</p><p>Sergio explains the different components of the architecture with a Micronaut application deployed to AWS.</p><p>The talk covers authentication servers, static websites, serverless functions, and API Gateways.</p><p>Attendees will discover that they can have an architecture that scales on demand, is easy to reason about, and boosts developer productivity.</p><h2>Elevator Pitch</h2><p>A serverless monolith is an architecture easy to reason about, scales on demand, and boosts developer productivity.</p>]]></description><guid>serverless-monoltih</guid><pubDate>Wed, 11 Jan 2023 06:23:25 GMT</pubDate></item><item><title>Micronaut Framework and AWS CDK</title><link>https://sergiodelamo.com/blog/aws-cdk-micronaut.html</link><description><![CDATA[<p>In this talk, <a href="https://sergiodelamo.com">Sergio del Amo</a> demonstrates the <a href="https://aws.amazon.com/cdk/">AWS CDK</a> integration with the <a href="https://micronaut.io">Micronaut Framework</a>.</p><p>The talk shows how to define infrastructure as Java code with Amazon CDK.</p><p>You will see how to create a serverless architecture with services such as AWS Certificate Manager, Route 53, Amazon API Gateway, AWS Lambda and DynamoDB, and Amazon Cognito.</p><p>Moreover, the talk demonstrates how to integrate Gatling load tests into the project.</p><p>Combining Micronaut Framework and Amazon CDK allows you to define a serverless architecture seamlessly with infrastructure defined as code, making it easy to replicate and deploy into other AWS accounts or environments.</p><p>Attendees will gain an understanding of Amazon CDK and how they can leverage it in combination with the Micronaut Framework.</p><p>## Elevator Pitch</p><p>The talk shows how to define infrastructure as Java code with Amazon CDK and how the Micronaut Framework integrates seamlessly with CDK.</p>]]></description><guid>aws-cdk-micronaut</guid><pubDate>Tue, 10 Jan 2023 20:44:16 GMT</pubDate></item><item><title>MicroStream and the Micronaut Framework Webinar</title><link>https://sergiodelamo.com/blog/micronaut-framework-microstream-webinar.html</link><description><![CDATA[<p>Video of the webinar about <a href="https://microstream.one">MicroStream</a> and the <a href="https://micronaut.io">Micronaut Framework</a> where I talked to  <a href="https://twitter.com/MarkusKett">Markus Kett</a> and <a href="https://twitter.com/FHHabermann">Florian Habermann</a>.</p><p><a href="https://www.youtube.com/watch?v=qoA1U-wsA6M">Go to the linked site</a></p>]]></description><guid>micronaut-framework-microstream-webinar</guid><pubDate>Sun, 08 Jan 2023 20:47:46 GMT</pubDate></item><item><title>Micronaut Podcast E016</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e016.html</link><description><![CDATA[<p>I talked to <a href="https://twitter.com/marksailes3">Mark Sailes</a>, senior specialist solutions architect at AWS, about Micronaut Framework 3.8.0 release and AWS Lambda Snapstart.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/016.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e016</guid><pubDate>Sat, 07 Jan 2023 21:08:23 GMT</pubDate></item><item><title>Record audio in QuickTime Player on Mac</title><link>https://sergiodelamo.com/blog/quicktime-audio-record.html</link><description><![CDATA[<p>You can make an audio-only recording with QuickTime Player.</p><p>If you are recording a Podcast with someone who does not have <a href="https://rogueamoeba.com/audiohijack/">Audio Hijack</a> installed, <a href="https://support.apple.com/en-gb/guide/quicktime-player/welcome/mac">QuickTime</a> is a fallback for your guests to record the audio on their end.</p><p><a href="https://support.apple.com/en-gb/guide/quicktime-player/qtpf25d6f827/mac">Go to the linked site</a></p>]]></description><guid>quicktime-audio-record</guid><pubDate>Thu, 05 Jan 2023 15:38:28 GMT</pubDate></item><item><title>Home Page with the latest blog posts</title><link>https://sergiodelamo.com/blog/new-home-page.html</link><description><![CDATA[<p>I moved the old home page contents to a <a href="https://sergiodelamo.com/projects.html">projects page</a>.</p><p>Now, my home page contains the latest 20 posts. Most of my entries are links to other places where I publish content (<a href="https://guides.micronaut.io">Micronaut Guides</a>, <a href="https://micronautpodcast.com">Micronaut Podcast</a>, <a href="https://www.youtube.com/playlist?list=PL9UkPxJVi4FT3gitylnECKe27Aar1c_Er">Youtube videos</a>, and interesting links. Now the linked title of these posts sends you directly to the external URL.</p><p>The ⚓ is a link to the permalink URL in my website for the post. The RSS Feed item's title links to my website, but the RSS Item adds a paragraph at the end of the post with the copy <em>Go to the linked site</em> pointing to the external URL.</p>]]></description><guid>new-home-page</guid><pubDate>Wed, 04 Jan 2023 12:51:41 GMT</pubDate></item><item><title>Loom Async video messaging</title><link>https://sergiodelamo.com/blog/loom-async-video-messaging.html</link><description><![CDATA[<p>Today, I got my first Loom video. It is exciting to see a wave of solutions for async communications. Either <a href="https://basecamp.com/features/automatic-check-ins">automatic check-ins</a> written or in a video.</p><p>A written check-in is better. However, I am the kind of person who is annoyed by getting audio messages on WhatsApp.</p><p><a href="https://www.loom.com">Go to the linked site</a></p>]]></description><guid>loom-async-video-messaging</guid><pubDate>Wed, 04 Jan 2023 06:39:21 GMT</pubDate></item><item><title>Online Talk at the Brighton Kotlin Meetup</title><link>https://sergiodelamo.com/blog/2023-01-brighton-kotlin-getting-stated-with-micronaut.html</link><description><![CDATA[<p>I will be giving the talk <a href="http://localhost/sergiodelamo.com/blog/presentation-micronaut-introduction.html">Getting Started with the Micronaut Framework</a> in the Brighton Kotlin user Group. I met <a href="https://twitter.com/leeturner">Lee Turner</a> last year in a Micronaut AWS Lambda training and he was kind enough to invite me.</p><p><a href="https://www.meetup.com/brighton-kotlin/events/290553314/">Go to the linked site</a></p>]]></description><guid>2023-01-brighton-kotlin-getting-stated-with-micronaut</guid><pubDate>Mon, 02 Jan 2023 17:19:55 GMT</pubDate></item><item><title>Micronaut Essentials 2022</title><link>https://sergiodelamo.com/blog/2022-12-micronaut-essentials.html</link><description><![CDATA[<p>This week, I delivered a 12-hour online course of Micronaut Essentials.</p><p>I delivered a 12-hour online course of <a href="https://micronaut.io/professional-training/micronaut-essentials/">Micronaut Essentails</a>.</p><p>I did a lot of live coding and interactions with the attendees. We wrote an application that used Micronaut Security, Micronaut Data JDBC, Micronaut Serialization, Problem+JSON, Micronaut Views, Open API, and Micronaut Reactor.</p><p>We will teach <a href="https://micronaut.io/professional-training/micronaut-essentials/9">Micronaut Essentials</a> again next year. I am looking forward to seeing you on the course.</p>]]></description><guid>2022-12-micronaut-essentials</guid><pubDate>Sat, 17 Dec 2022 08:18:42 GMT</pubDate></item><item><title>Partner of Object Computing Inc</title><link>https://sergiodelamo.com/blog/partnert-of-objectcomputing.html</link><description><![CDATA[<p>Earlier this month, I flew to St. Louis to attend Object Computing's holiday partner. <a href="https://www.linkedin.com/feed/update/urn:li:activity:7008850408336953344/">Gina M Bremehr, President and COO, welcomed me as a new partner of Object Computing Inc</a> - a milestone in my professional career.</p><blockquote><p>Excited and grateful to welcome Teresa Cathey, Tino Nwamba, Sergio del Amo Caballero, Kianna Reed, and Robert Elfanbaum as Object Computing Partners and Equity Shareholders. Thank you to each of you for your outstanding contributions and leadership.</p></blockquote><blockquote><p>All of us is better than any one of us.Innovate courageously and mindfully.Share it back.</p></blockquote><p>What a journey since my <a href="https://sergiodelamo.com/blog/my-first-day-at-oci.html">first day at OCI</a>.</p>]]></description><guid>partnert-of-objectcomputing</guid><pubDate>Sat, 17 Dec 2022 07:36:00 GMT</pubDate></item><item><title>Feedback from AWS Lambda with Micronaut Framework workshop in London.</title><link>https://sergiodelamo.com/blog/micronaut-aws-lambda-training-london.html</link><description><![CDATA[<p>On October 5th, I taught an AWS Lambda with Micronaut Framework workshop in London.</p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">Had an awesome day up in London meeting <a href="https://twitter.com/MarkSailes3?ref_src=twsrc%5Etfw">@MarkSailes3</a>, <a href="https://twitter.com/sdelamo?ref_src=twsrc%5Etfw">@sdelamo</a> and <a href="https://twitter.com/mmeckes?ref_src=twsrc%5Etfw">@mmeckes</a> and learning about AWS Lambda and <a href="https://twitter.com/micronautfw?ref_src=twsrc%5Etfw">@micronautfw</a> Looking forward to trying this out on a few projects.</p>&mdash; Lee Turner (@leeturner) <a href="https://twitter.com/leeturner/status/1577756271322828805?ref_src=twsrc%5Etfw">October 5, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>]]></description><guid>micronaut-aws-lambda-training-london</guid><pubDate>Fri, 18 Nov 2022 15:50:25 GMT</pubDate></item><item><title>Pics from Barcelona JBCNConf</title><link>https://sergiodelamo.com/blog/pics-from-jbcnconf-barcelona-2022.html</link><description><![CDATA[<p>Some pictures from last July JBCNConf 2022.</p><p>I presented a workshop about <a href="https://micronaut-projects.github.io/micronaut-security/latest/guide/">Micronaut Security</a> at <a href="https://www.jbcnconf.com/2022/">JBCNConf 2022</a>. They <a href="https://www.flickr.com/photos/barcelonajug/collections/72157720937175671/">published many pictures</a>, and I fetched a couple where I appeared.</p><p><img src="https://images.sergiodelamo.com/sergio-del-amo-talking-about-micronaut-security-filter-at-barcelona-jbcnconf-2022.jpg" alt="Sergio del Amo Talking about Micronaut Security Filter at Barcelona JBCNConf 2022" /><img src="https://images.sergiodelamo.com/sergio-del-amo-talking-about-basic-auth-and-micronaut-at-barcelona-jbcnconf-2022.jpg" alt="Sergio del Amo Talking about basic authentication within Micronaut Security at Barcelona JBCNConf 2022" /></p><p>It was great to meet with the core Micronaut Framework team. We went for dinner at <a href="https://bocagrande.cat/es/boca-grande-es/">Boca Grande</a>, a really nice restaurant.</p><p><img src="https://images.sergiodelamo.com/sergio-delamo-and-alvaro-sanchez-mariscal-at-jbcnconf.jpg" alt="Sergio del amo and Álvaro Sánchez Mariscal at JBCN Conf" /><img src="https://images.sergiodelamo.com/sergio-del-amo-graeme-rocher-alvaro-sanchez-at-boca-grande.jpg" alt="Sergio del Amo with Graeme Rocher and Álvaro Sánchez Mariscal out for dinner at Boca Negra" /><img src="https://images.sergiodelamo.com/sergio-del-amo-graeme-rocher-alvaro-sanchez-and-dmitry-alexandrov.jpg" alt="Sergio del Amo, Graeme Rocher, Álvaro Sánchez Mariscal and Dmitry Alexandrov at JBCNConf 2022" /></p>]]></description><guid>pics-from-jbcnconf-barcelona-2022</guid><pubDate>Sun, 13 Nov 2022 07:19:32 GMT</pubDate></item><item><title>Micronaut Podcast E015</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e015.html</link><description><![CDATA[<p>I talked to Markus Kett about Micronaut Object Storage.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/015.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e015</guid><pubDate>Mon, 07 Nov 2022 17:20:01 GMT</pubDate></item><item><title>Event Sourcing</title><link>https://sergiodelamo.com/blog/event-sourcing.html</link><description><![CDATA[<p>Collections of links to event sourcing</p><ul><li><a href="https://microservices.io/patterns/data/event-sourcing.html">Pattern: Event sourcing</a></li><li><a href="https://event-driven.io/en/when_not_to_use_event_sourcing/">When not to use Event Sourcing?</a></li><li><a href="https://blog.softwaremill.com/3-reasons-to-adopt-event-sourcing-89cb855453f6">3 reasons to adopt Event Sourcing</a></li><li><a href="https://www.redhat.com/architect/pros-and-cons-event-sourcing-architecture-pattern">The pros and cons of the Event Sourcing architecture pattern</a></li><li><a href="https://envylabs.com/insights/what-is-event-sourcing-and-what-are-its-advantages/">Event Sourcing 101: The Advantages of Immutable Events</a></li><li><a href="https://www.lagomframework.com/documentation/1.6.x/java/ESAdvantage.html">Advantages of Event Sourcing</a></li><li><a href="https://www.baeldung.com/cqrs-event-sourcing-java">CQRS and Event Sourcing in Java</a></li><li><a href="https://cqrs.nu/Faq/event-sourcing">event sourcing FAQ</a></li></ul>]]></description><guid>event-sourcing</guid><pubDate>Fri, 04 Nov 2022 15:37:32 GMT</pubDate></item><item><title>JCON 2022 - Micronaut Framework and MicroStream Java-native persistence engine</title><link>https://sergiodelamo.com/blog/jcon-2022-micronaut-microstream.html</link><description><![CDATA[<p>Video of my talk at the online conference JCON 2022.</p><p><a href="https://www.youtube.com/watch?v=5W6oVj0h6rQ">Go to the linked site</a></p>]]></description><guid>jcon-2022-micronaut-microstream</guid><pubDate>Thu, 03 Nov 2022 09:20:42 GMT</pubDate></item><item><title>JCON 2022 - Getting Started with the Micronaut Framework</title><link>https://sergiodelamo.com/blog/jcon-2022-micronaut-getting-started.html</link><description><![CDATA[<p>Video of my talk at the online conference JCON 2022.</p><p><a href="https://www.youtube.com/watch?v=gvi-F5BDPXc">Go to the linked site</a></p>]]></description><guid>jcon-2022-micronaut-getting-started</guid><pubDate>Thu, 03 Nov 2022 09:20:42 GMT</pubDate></item><item><title>Google Workspace/Cloud Identity: Sign in Issues</title><link>https://sergiodelamo.com/blog/google-workspace-recovery.html</link><description><![CDATA[<p><a href="https://www.theverge.com/2022/1/19/22891509/g-suite-legacy-free-google-apps-workspace-upgrade">Google moved the free &quot;legacy&quot; G Suite accounts to a paid tier</a>. If you don't know who the owner user is but control the domain, you <a href="https://toolbox.googleapps.com/apps/recovery/form">can recover the account using Google Workspace/Cloud Identity: Sign-in Issues form</a>.</p>]]></description><guid>google-workspace-recovery</guid><pubDate>Wed, 26 Oct 2022 08:51:27 GMT</pubDate></item><item><title>Micronaut Framework y AWS Lambda</title><link>https://sergiodelamo.com/blog/micronaut-aws-lambda-barcelona-jug.html</link><description><![CDATA[<p>Video of my talk at <a href="https://www.barcelonajug.org">Barcelona JUG</a> about Micronaut framework and AWS Lambda. The talk is in Spanish.</p><p><a href="https://www.youtube.com/watch?v=S3qYOVNaKS8">Go to the linked site</a></p>]]></description><guid>micronaut-aws-lambda-barcelona-jug</guid><pubDate>Thu, 13 Oct 2022 19:14:35 GMT</pubDate></item><item><title>Micronaut Podcast E014</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e014.html</link><description><![CDATA[<p>I talked to Álvaro Sánchez-Mariscal about Micronaut Object Storage.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/014.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e014</guid><pubDate>Tue, 11 Oct 2022 17:20:01 GMT</pubDate></item><item><title>Groovy CLI application for Podcast reporting</title><link>https://sergiodelamo.com/blog/podcast-reporting-with-a-groovy-cli-app.html</link><description><![CDATA[<p>I wrote a small <a href="https://micronaut.io">Micronaut Framework</a> command line <a href="https://github.com/micronaut-advocacy/cli-podcastreport">application to output a CSV file with a podcast's episode titles, authors, and release dates</a>. Micronaut CLI applications use <a href="https://picocli.info">Picocli</a>.</p><p>Working with XML and <a href="https://groovy-lang.org">Apache Groovy</a> is easy.</p><p>The core functionality is  only a few lines of Apache Groovy code:</p><pre><code class="language-groovy">@CompileDynamicvoid run() {    String text = new URL(url).text    def rss = new XmlSlurper().parseText(text)    List&lt;Episode&gt; episodeList = []    String publication = DateTimeFormatter.ISO_DATE.format(        DateTimeFormatter.ofPattern(&quot;EEE, dd MMM yyyy HH:mm:ss z&quot;)            .parse(it.pubDate as String))    rss.channel.item.each {        episodeList &lt;&lt; new Episode(title: it.title,                                    author: it.author,                                    publication: publication)    String result = episodeList.sort({a, b -&gt; b.publication &lt;=&gt; a.publication})        .collect {it -&gt; &quot;${it.title},${it.author},${it.publication}&quot;}.join(&quot;\n&quot;)    if (verbose) {        println result    }    output.text = result}</code></pre><p><a href="https://github.com/micronaut-advocacy/cli-podcastreport">Go to the linked site</a></p>]]></description><guid>podcast-reporting-with-a-groovy-cli-app</guid><pubDate>Mon, 10 Oct 2022 17:11:44 GMT</pubDate></item><item><title>CLI cloc - Count Lines of Code</title><link>https://sergiodelamo.com/blog/cloc.html</link><description><![CDATA[<blockquote><p>cloc counts blank lines, comment lines, and physical lines of source code in many programming languages.</p></blockquote><pre><code>% cloc .     409 text files.     305 unique files.                                               269 files ignored.github.com/AlDanial/cloc v 1.84  T=0.29 s (981.3 files/s, 62822.2 lines/s)-------------------------------------------------------------------------------Language                     files          blank        comment           code-------------------------------------------------------------------------------HTML                            42              4              0           5065Java                           118            828             33           4385XML                             39             19             13           3050Bourne Shell                    11            252            798           1150DOS Batch                       10            218             20            644YAML                            20              6              6            512Gradle                          33             42              0            410CSS                              2             49              0            214JSON                             6              0              0            182JavaScript                       2             46              1            154Markdown                         1             35              0             46-------------------------------------------------------------------------------SUM:                           284           1499            871          15812-------------------------------------------------------------------------------</code></pre><p><a href="https://github.com/AlDanial/cloc">Go to the linked site</a></p>]]></description><guid>cloc</guid><pubDate>Mon, 03 Oct 2022 06:04:41 GMT</pubDate></item><item><title>Apache Groovy script to list Micronaut Guides</title><link>https://sergiodelamo.com/blog/groovy-script-to-list-micronaut-guides.html</link><description><![CDATA[<p>Apache Groovy is extremely powerful to write a script quickly</p><pre><code class="language-groovy">import groovy.json.JsonSlurperclass Guide {    String title    String authors    String publicationDate        String getQuarter() {        String month = publicationDate.substring(5,7)        if (month == '01' || month == '02' || month == '03') {            return 'Q1'        } else if (month == '04' || month == '05' || month == '06') {           return 'Q2'        } else if (month == '07' || month == '08' || month == '09') {           return 'Q3'        } else {            return 'Q4'        }    }}def result = new JsonSlurper().parseText(new URL(&quot;https://guides.micronaut.io/latest/guides.json&quot;).text)List&lt;Guide&gt; guides = result.collect { new Guide(title: it.title, publicationDate: it.publicationDate, authors: it.authors.join('/')) }guides = guides.sort({ a, b -&gt;     a.publicationDate &lt;=&gt; b.publicationDate }).reverse()File f = new File(&quot;/Users/sdelamo/Downloads/guides.csv&quot;)String text = &quot;&quot;for (Guide guide : guides) {text += guide.quarter + &quot;,&quot; + guide.title + &quot;,Guide,&quot;+ guide.authors +&quot;,&quot;+ guide.publicationDate +&quot;\n&quot;}f.text = text</code></pre>]]></description><guid>groovy-script-to-list-micronaut-guides</guid><pubDate>Fri, 30 Sep 2022 17:40:17 GMT</pubDate></item><item><title>Install AWS CDK CLI</title><link>https://sergiodelamo.com/blog/install-aws-cdk.html</link><description><![CDATA[<p>To install AWS CDK CLI in MacOS you need to install npm first.</p><h2>Install NPM</h2><p>Install <code>npm</code>.  You can <a href="https://www.npmjs.com/package/homebrew">install in MacOS with homebrew</a>.</p><h2>Install AWS CDK CLI</h2><p><a href="https://docs.aws.amazon.com/cdk/v2/guide/cli.html">Install AWS CDK CLI</a></p><pre><code>npm install -g aws-cdk  ``</code></pre>]]></description><guid>install-aws-cdk</guid><pubDate>Thu, 29 Sep 2022 09:59:48 GMT</pubDate></item><item><title>Install Micronaut CLI via SDKMAN.</title><link>https://sergiodelamo.com/blog/installing-the-micronaut-cli.html</link><description><![CDATA[<p>The easiest way to install the Micronaut CLI is to use SDKMan.</p><p><a href="https://sdkman.io">SDKMAN</a></p><blockquote><p>SDKMAN! is a tool for managing parallel versions of multiple Software Development Kits on most Unix-based systems. It provides a convenient Command Line Interface (CLI) and API for installing, switching, removing, and listing Candidates.</p></blockquote><h2>Install the Latest</h2><p>To install the latest version of Micronaut CLI, run:</p><pre><code>sdkman install micronaut</code></pre><h2>List Micronaut CLI candidates</h2><p>To get a list of the Micronaut CLI available versions, type:</p><pre><code>sdk list micronaut</code></pre><pre><code>================================================================================Available Micronaut Versions================================================================================ &gt; * 3.7.1               3.1.0               2.2.1             * 1.2.5             * 3.7.0             * 3.0.3             * 2.2.0             * 1.2.4               3.6.4               3.0.2             * 2.1.4             * 1.2.3               3.6.3               3.0.1             * 2.1.3             * 1.2.2               3.6.2             * 3.0.0             * 2.1.2               1.2.1             * 3.6.1               3.0.0-RC1           2.1.1             * 1.2.0             * 3.6.0               3.0.0-M5          * 2.1.0               1.2.0.RC2           3.5.5               3.0.0-M2          * 2.0.3               1.2.0.RC1         * 3.5.4               3.0.0-M1            2.0.2               1.1.4             * 3.5.3             * 2.5.13            * 2.0.1               1.1.3             * 3.5.2               2.5.12            * 2.0.0               1.1.2             * 3.5.1               2.5.11            * 2.0.0.RC2         * 1.1.1             * 3.5.0               2.5.9             * 2.0.0.RC1           1.1.0               3.4.4               2.5.8             * 2.0.0.M3            1.1.0.RC2           3.4.3               2.5.7             * 2.0.0.M2            1.1.0.RC1           3.4.2               2.5.6             * 2.0.0.M1            1.1.0.M2            3.4.1             * 2.5.5             * 1.3.7               1.1.0.M1          * 3.4.0             * 2.5.4             * 1.3.6               1.0.5               3.3.4             * 2.5.3               1.3.5               1.0.4               3.3.3             * 2.5.1             * 1.3.4               1.0.3               3.3.1             * 2.5.0             * 1.3.3               1.0.2               3.3.0               2.4.4             * 1.3.2               1.0.1               3.2.7               2.4.3             * 1.3.1               1.0.0               3.2.6             * 2.4.2             * 1.3.0               1.0.0.RC3           3.2.5             * 2.4.1             * 1.3.0.RC1           1.0.0.RC2           3.2.4             * 2.4.0             * 1.3.0.M2            1.0.0.RC1           3.2.3             * 2.3.4             * 1.3.0.M1            1.0.0.M4            3.2.2               2.3.3               1.2.11              1.0.0.M3            3.2.1             * 2.3.2               1.2.10              1.0.0.M2            3.2.0             * 2.3.1               1.2.9               1.0.0.M1            3.1.4             * 2.3.0             * 1.2.8                                   3.1.3             * 2.2.3             * 1.2.7                                 * 3.1.1               2.2.2             * 1.2.6                              ================================================================================+ - local version* - installed&gt; - currently in use================================================================================</code></pre><h2>Install a specific version</h2><p>You can install a particular version of Micronaut CLI with:</p><p><code>sdk install micronaut 3.6.4</code></p><h2>To use a specific version</h2><p>To use a specific version of Micronaut CLI in a terminal window:</p><p><code>sdk use micronaut 3.7.1</code>.</p><p>You can check the version of Micronaut CLI you are running by typing the following command:</p><h2>Verify the version of Micronaut CLI you are running</h2><pre><code>% mn --versionMicronaut Version: 3.7.1</code></pre>]]></description><guid>installing-the-micronaut-cli</guid><pubDate>Thu, 29 Sep 2022 09:48:32 GMT</pubDate></item><item><title>How to build a GraalVM Native executable of a Micronaut application from an Apple Silicon Mac and deploy it to AWS Lambda ARM architecture.</title><link>https://sergiodelamo.com/blog/apple-silicon-micronaut-framework-graalvm-native-image-aws-lambda-cdk-function-url.html</link><description><![CDATA[<p>This post highlights the necessary changes to the application build to deploy a GraalVM Native executable to AWS Lambda Custom Runtime with an arm64 architecture.</p><h2>Lambda instruction set architectures</h2><p><a href="https://docs.aws.amazon.com/lambda/latest/dg/foundation-arch.html">AWS Lambda supports multiple architectures</a>:</p><blockquote><p>The instruction set architecture of a Lambda function determines the type of computer processor that Lambda uses to run the function. Lambda provides a choice of instruction set architectures:</p></blockquote><blockquote><p><code>arm64</code> – 64-bit ARM architecture, for the AWS Graviton2 processor. The arm64 architecture is available in most AWS Regions.<code>x86_64</code> – 64-bit x86 architecture, for x86-based processors.</p></blockquote><h2>Java Version for macOS ARM 64.</h2><p>Most modern <a href="https://support.apple.com/en-us/HT211814">Mac computers run Apple silicon</a>. You need a Java Version for macOS ARM 64.</p><p>I use the highest Corretto version supported in the AWS <a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html">Lambda Runtimes</a> when working with Lambda. It is easy to select if you use <a href="https://sdkman.io">SDKMAN</a>.</p><pre><code>================================================================================Available Java Versions for macOS ARM 64bit================================================================================ Vendor        | Use | Version      | Dist    | Status     | Identifier-------------------------------------------------------------------------------- Corretto      |     | 19           | amzn    |            | 19-amzn                            |     | 17.0.4       | amzn    |            | 17.0.4-amzn                        | &gt;&gt;&gt; | 11.0.16      | amzn    | installed  | 11.0.16-amzn                       |     | 8.0.342      | amzn    |            | 8.0.342-amzn </code></pre><h2>Generate the Micronaut Application for AWS Lambda</h2><p>The easiest way to generate Micronaut Applications is to use Micronaut CLI or <a href="https://launch.micronaut.io/">Micronaut Launch</a>.</p><p>Generate a <a href="https://micronaut.io/launch?type=FUNCTION&amp;features=graalvm&amp;features=aws-cdk&amp;features=aws-lambda-function-url&amp;features=aws-lambda"><code>Function Application for Serverless</code> with the following features: <code>graalvm</code>, <code>aws-cdk</code>, <code>aws-lambda</code>,  and <code>aws-lambda-function-url</code></a>.</p><h2>Selecting ARM Architecture</h2><p>If you use Cloud Formation or <a href="https://aws.amazon.com/cdk/">AWS Cloud Development Kit</a>, to create your infrastructure, select ARM as the architecture.</p><pre><code class="language-java">......  .logRetention(RetentionDays.ONE_WEEK)  .architecture(Architecture.ARM_64)  .build();</code></pre><h2>GraalVM native image generation for Lambda with Gradle</h2><p><a href="https://micronaut-projects.github.io/micronaut-gradle-plugin/latest/#_deploying_to_aws_lambda_as_graalvm_native_image">Micronaut Gradle Plugin</a> provides a Gradle task <code>buildNativeLambda</code> to help with the GraalVM Native executable generation.</p><blockquote><p>If you are interested in deploying your Micronaut application to AWS Lambda using GraalVM you only need to set the runtime to lambda and execute ./gradlew buildNativeLambda. This task will generate a GraalVM native image inside a Docker container and then it will create the file build/libs/your-app.zip file ready to be deployed to AWS Lambda using a custom runtime.</p></blockquote><h2>Controlling the target architecture of Lambda GraalVM native image</h2><p><a href="https://micronaut-projects.github.io/micronaut-gradle-plugin/latest/#_deploying_to_aws_lambda_as_graalvm_native_image">Micronaut Gradle Plugin</a> offers automatic detection:</p><blockquote><p>The plugin will detect the host operating system architecture (based on the <code>os.arch</code> Java system property) and will install the corresponding GraalVM binary distribution inside the Docker image. This means that when running packaging from an X86_64 (Intel/AMD) machine, the produced native image will be an amd64 binary, whilst on an ARM host (such as the new Mac M1) it will be an aarch64 binary.</p></blockquote><p>Moreover, you can control it manually:</p><pre><code>dockerfileNative {    graalArch.set(org.apache.tools.ant.taskdefs.condition.Os.isArch(&quot;aarch64&quot;) ? &quot;aarch64&quot; : &quot;amd64&quot;)}</code></pre><h2>GraalVM native image generation for Lambda with Maven</h2><p>You can achieve the native images for Lambda with the <a href="https://micronaut-projects.github.io/micronaut-maven-plugin/latest/examples/package.html#building_jvm_based_docker_images">Micronaut Maven Plugin</a>:</p><pre><code>mvn package -Dpackaging=docker -Dmicronaut.runtime=lambda</code></pre><h2>Force latest version of docker-java</h2><p>When working with Apple Silicon and ARM architecture you need force the Micronaut Gradle plugin to use the latest version of <a href="https://github.com/docker-java/docker-java#docker-java">Docker-java</a>.</p><pre><code>buildscript {    dependencies {        classpath(&quot;com.github.docker-java:docker-java-transport-httpclient5:3.2.13&quot;) {            because(&quot;M1 macs need a later version of JNA&quot;)        }    }}</code></pre><h2>Mixing Architectures</h2><p>It is impossible to generate a GraalVM native Image of your Micronaut Application in an Apple Silicon Mac and deploy it to an AWS Lambda Custom Runtime using x86 architecture.</p><h2>Deployment instructions</h2><p>The <code>README.md</code> file of the <a href="https://micronaut.io/launch?type=FUNCTION&amp;features=graalvm&amp;features=aws-cdk&amp;features=aws-lambda-function-url&amp;features=aws-lambda">generated application</a> contains deployment instructions.</p>]]></description><guid>apple-silicon-micronaut-framework-graalvm-native-image-aws-lambda-cdk-function-url</guid><pubDate>Thu, 29 Sep 2022 09:34:33 GMT</pubDate></item><item><title>GraalVM Native executable fo a Micronaut application with AWS CDK, AWS Lambda (Custom Runtime), and function URL.</title><link>https://sergiodelamo.com/blog/micronaut-framework-graalvm-native-image-aws-lambda-cdk-function-url.html</link><description><![CDATA[<p>Generate and deploy a Micronaut Application as a GraalVM Native executable with a multi-project build with CDK and a Lambda function associated with an HTTP(s) endpoint via a function URL.</p><h2>Generate the application</h2><p>The easiest way to generate Micronaut Applications is to use Micronaut CLI or <a href="https://launch.micronaut.io/">Micronaut Launch</a>.</p><p>Generate a <a href="https://micronaut.io/launch?type=FUNCTION&amp;features=graalvm&amp;features=aws-cdk&amp;features=aws-lambda-function-url&amp;features=aws-lambda"><code>Function Application for Serverless</code> with the following features: <code>graalvm</code>, <code>aws-cdk</code>, <code>aws-lambda</code>,  and <code>aws-lambda-function-url</code></a>.</p><p>The generated project is a multi-project build with a structure similar to the following listing (if you selected Gradle as your build tool):</p><pre><code>.├── README.md├── app│   ├── build.gradle│   └── src│       ├── main│       │   ├── java│       │   │   └── com│       │   │       └── example│       │   │           └── FunctionRequestHandler.java│       │   └── resources│       │       ├── application.yml│       │       └── logback.xml│       └── test│           └── java│               └── com│                   └── example│                       └── FunctionRequestHandlerTest.java├── gradle│   └── wrapper│       ├── gradle-wrapper.jar│       └── gradle-wrapper.properties├── gradle.properties├── gradlew├── gradlew.bat├── infra│   ├── build.gradle│   ├── cdk.json│   └── src│       ├── main│       │   └── java│       │       └── com│       │           └── example│       │               ├── AppStack.java│       │               └── Main.java│       └── test│           └── java│               └── com│                   └── example│                       └── AppStackTest.java├── micronaut-cli.yml├── settings.gradle└── test-lambda.sh</code></pre><p>There are two folders:</p><ul><li><code>app</code>  - where your application code lives.</li><li><code>infra</code> - where the code describing the resources being created resides.</li></ul><h2>Infrastructure as Code</h2><p>To generate the infrastructure, we leverage <a href="https://aws.amazon.com/cdk/">AWS Cloud Development Kit</a>.</p><blockquote><p>The AWS Cloud Development Kit (AWS CDK) is an open-source software development framework to define your cloud application resources using familiar programming languages.</p></blockquote><p>The infrastructure is created via:</p><p><code>infra/src/main/java/com/example/AppStack.java</code></p><p>It creates an AWS Lambda Function with Custom Runtime, with one-week log retention, 512 MB of memory, timeout of 10s, and active tracing.</p><h2>Deployment instructions</h2><p>The <code>README.md</code> file contains deployment instructions. You need to <a href="install-aws-cdk.html">install AWS CDK CLI</a> to deploy.</p><pre><code>./gradlew :app:buildNativeLambda./gradlew testcd infracdk synthcdk deploy</code></pre><p>If you have never run CDK commands in the AWS account, you must first run <code>cdk bootstrap</code>.</p><h2>Outputs</h2><p>After deployment, the console output shows something similar to:</p><pre><code>...Outputs:MicronautAppStack.MnTestApiUrl = https://5xh3ybw2k6gna4bwc5ezjxyywq0eyutp.lambda-url.us-east-1.on.aws/</code></pre><p>Hit the endpoint, and you get a 200 Hello World JSON response:</p><pre><code>HTTP/1.1 200 OKDate: Thu, 29 Sep 2022 09:57:28 GMTContent-Type: application/jsonContent-Length: 25Connection: keep-alivex-amzn-RequestId: ee116b51-596f-48a7-9435-7a0ec5927d65X-Amzn-Trace-Id: root=1-63356c07-6630a41c766c6847644902f6;sampled=1{&quot;message&quot;:&quot;Hello World&quot;}</code></pre><h2>Handler</h2><p>The Lambda Handler is at:</p><p><code>app/src/main/java/com/example/FunctionRequestHandler.java</code></p><p>It uses <a href="https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-events">AWS Lambda Java Events</a> to receive and respond to the HTTP Trigger.</p><h2>Cold Start Performance</h2><p>You can visit the AWS Console and go to the <code>Monitor</code> tab. In the <code>Traces</code> section, you can find information about the performance of your function for cold starts.</p><pre><code class="language-java">Initialization: 382msInvocation: 31ms</code></pre><h2>Cleanup</h2><p>The <code>README.md</code> contains instructions to clean up the resources you created.</p><pre><code>cd infracdk destroy</code></pre>]]></description><guid>micronaut-framework-graalvm-native-image-aws-lambda-cdk-function-url</guid><pubDate>Thu, 29 Sep 2022 09:34:33 GMT</pubDate></item><item><title>Micronaut Framework AWS CDK, AWS Lambda (Java Runtime), and function URLs</title><link>https://sergiodelamo.com/blog/micronaut-framework-aws-lambda-cdk-function-url.html</link><description><![CDATA[<p>Generate a Micronaut Application with a multi-project build with CDK and a Lambda function associated with an HTTP(s) endpoint via a function URL.</p><h2>Generate the application</h2><p>The easiest way to generate Micronaut Applications is to use Micronaut CLI or <a href="https://launch.micronaut.io/">Micronaut Launch</a>.</p><p>Generate a <a href="https://micronaut.io/launch?type=FUNCTION&amp;features=aws-cdk&amp;features=aws-lambda-function-url&amp;features=aws-lambda"><code>Function Application for Serverless</code> with the following features <code>aws-cdk</code>, <code>aws-lambda</code>,  and <code>aws-lambda-function-url</code></a>.</p><p>The generated project is a multi-project build with a structure similar to the following listing (if you selected Gradle as your build tool):</p><pre><code>.├── README.md├── app│   ├── build.gradle│   └── src│       ├── main│       │   ├── java│       │   │   └── com│       │   │       └── example│       │   │           └── FunctionRequestHandler.java│       │   └── resources│       │       ├── application.yml│       │       └── logback.xml│       └── test│           └── java│               └── com│                   └── example│                       └── FunctionRequestHandlerTest.java├── gradle│   └── wrapper│       ├── gradle-wrapper.jar│       └── gradle-wrapper.properties├── gradle.properties├── gradlew├── gradlew.bat├── infra│   ├── build.gradle│   ├── cdk.json│   └── src│       ├── main│       │   └── java│       │       └── com│       │           └── example│       │               ├── AppStack.java│       │               └── Main.java│       └── test│           └── java│               └── com│                   └── example│                       └── AppStackTest.java├── micronaut-cli.yml├── settings.gradle└── test-lambda.sh</code></pre><p>There are two folders:</p><ul><li><code>app</code>  - where your application code lives.</li><li><code>infra</code> - where the code describing the resources being created resides.</li></ul><h2>Infrastructure as Code</h2><p>To generate the infrastructure, we leverage <a href="https://aws.amazon.com/cdk/">AWS Cloud Development Kit</a>.</p><blockquote><p>The AWS Cloud Development Kit (AWS CDK) is an open-source software development framework to define your cloud application resources using familiar programming languages.</p></blockquote><p>The infrastructure is created via:</p><p><code>infra/src/main/java/com/example/AppStack.java</code></p><p>It creates an AWS Lambda Function with Java 11 Runtime, with one-week log retention, 512 MB of memory, timeout of 10s, and active tracing.</p><h2>Deployment instructions</h2><p>The <code>README.md</code> file contains deployment instructions. You need to <a href="install-aws-cdk.html">install AWS CDK CLI</a> to deploy.</p><pre><code>./gradlew shadowJar./gradlew testcd infracdk synthcdk deploy</code></pre><p>If you have never run CDK commands in the AWS account, you must first run <code>cdk bootstrap</code>.</p><h2>Outputs</h2><p>After deployment, the console output shows something similar to:</p><pre><code>...Outputs:MicronautAppStack.MnTestApiUrl = https://6mvj6q5jjore5kxyh2uh52k5fa0yyuwq.lambda-url.us-east-1.on.aws/</code></pre><p>Hit the endpoint, and you get a 200 Hello World JSON response:</p><pre><code>curl -i https://6mvj6q5jjore5kxyh2uh52k5fa0yyuwq.lambda-url.us-east-1.on.aws/HTTP/1.1 200 OKDate: Thu, 29 Sep 2022 09:17:39 GMTContent-Type: application/jsonContent-Length: 25Connection: keep-alivex-amzn-RequestId: 8ce868d1-b6a5-4aca-9801-90e7f9c1ac48X-Amzn-Trace-Id: root=1-633562b0-70c6fad813e10b370d55ba83;sampled=1{&quot;message&quot;:&quot;Hello World&quot;}%        </code></pre><h2>Handler</h2><p>The Lambda Handler is at:</p><p><code>app/src/main/java/com/example/FunctionRequestHandler.java</code></p><p>It uses <a href="https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-events">AWS Lambda Java Events</a> to receive and respond to the HTTP Trigger.</p><h2>Cold Start Performance</h2><p>You can visit the AWS Console and go to the <code>Monitor</code> tab. In the <code>Traces</code> section, you can find information about the performance of your function for cold starts.</p><pre><code class="language-java">Initialization: 2.17sInvocation: 190ms</code></pre><h2>Cleanup</h2><p>The <code>README.md</code> contains instructions to clean up the resources you created.</p><pre><code>cd infracdk destroy</code></pre>]]></description><guid>micronaut-framework-aws-lambda-cdk-function-url</guid><pubDate>Thu, 29 Sep 2022 09:34:33 GMT</pubDate></item><item><title>London Workshop about AWS Lambda and the Micronaut Framework</title><link>https://sergiodelamo.com/blog/london-oct-5th-aws-lambda-and-micronaut-framework.html</link><description><![CDATA[<p>I will be delivering a workshop in the AWS London offices next week.</p><blockquote><p>Through a combination of lectures, live coding demonstrations, AWS interaction, and hands-on exercises, participants learn:How AWS Lambda functions written with the Micronaut framework can be triggered with AWS Events (such as an S3 or DynamoDB event)How integrating Amazon API Gateway and AWS Lambda enables you to write your applications as you would with a Netty runtime (i.e., write your applications as you usually do; run them in AWS Lambda)How to deploy a Micronaut application as a GraalVM native image to AWS Lambda Java runtime or to a custom runtimeHow leveraging certain characteristics of Micronaut applications, including fast startup, low memory consumption, and GraalVM integration, can help you work around cold startups in AWS Lambda.</p></blockquote><p>It is an invite only event. If you are interested please <a href="https://jumpstartyourjavaapplicationsw.splashthat.com">fill the form</a> in the website.</p><p><a href="https://jumpstartyourjavaapplicationsw.splashthat.com">Go to the linked site</a></p>]]></description><guid>london-oct-5th-aws-lambda-and-micronaut-framework</guid><pubDate>Thu, 29 Sep 2022 09:20:19 GMT</pubDate></item><item><title>Micronaut Podcast E013</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e013.html</link><description><![CDATA[<p>I talked to Álvaro Sánchez-Mariscal about Micronaut Maven Plugin.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/013.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e013</guid><pubDate>Tue, 27 Sep 2022 17:20:01 GMT</pubDate></item><item><title>Micronaut Podcast E012</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e012.html</link><description><![CDATA[<p>Szymon Stepniak interviews Graeme Rocher at GraalVM Community Meetup.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/012.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e012</guid><pubDate>Wed, 21 Sep 2022 17:20:01 GMT</pubDate></item><item><title>Java User Groups List</title><link>https://sergiodelamo.com/blog/java-user-groups.html</link><description><![CDATA[<p>List of JUGs around the world</p><p><a href="https://dev.java/community/jugs/">Go to the linked site</a></p>]]></description><guid>java-user-groups</guid><pubDate>Fri, 22 Jul 2022 07:54:31 GMT</pubDate></item><item><title>&#x1f468;&#x1f3fb;‍&#x1f3eb; Teaching Jumpstart your Micronaut Applications with AWS Lambda</title><link>https://sergiodelamo.com/blog/micronaut-aws-lambda-online-course-july-2022.html</link><description><![CDATA[<p>In July, I will teach an online three-day course about Micronaut Framework and AWS Lambda.</p><p>🗓 Dates: July 11 to July 13<br />⏰ Time: 9:00 a.m. to 12:00 p.m. CDT<br />📍 Location: Online<br />🧑‍🏫 Instructor: Yours truly<br />💵 Registration Fee: $200.00 USD</p><p><a href="https://objectcomputing.com/training/register/offering/336">Enroll Now</a></p><blockquote><p>The following topics are covered in this workshop:</p></blockquote><blockquote><ul><li>Micronaut and AWS Lambda Java Runtime</li><li>Micronaut GraalVM Native Images and AWS Lambda</li><li>Infrastructure generation with CDK</li><li>Micronaut and AWS Lambda / API Gateway</li><li>Micronaut and AWS Lambda / API Gateway WebSockets</li><li>Micronaut and AWS Lambda Function Url</li><li>Tracing with X-Ray</li><li>NoSQL persistence with DynamoDB and Micronaut</li><li>Uploading files to S3 with Micronaut</li><li>Cron Jobs with Lambda</li><li>Monitoring with Cloud Watch</li><li>Handle authentication with Cognito</li></ul></blockquote><p><a href="https://objectcomputing.com/services/training/catalog/micronaut-training/micronaut-aws-lambda">Go to the linked site</a></p>]]></description><guid>micronaut-aws-lambda-online-course-july-2022</guid><pubDate>Fri, 01 Jul 2022 16:10:16 GMT</pubDate></item><item><title>How to signup to a AWS Account?</title><link>https://sergiodelamo.com/blog/signup-aws.html</link><description><![CDATA[<p><a href="https://portal.aws.amazon.com/billing/signup#/start/email">Go to the linked site</a></p>]]></description><guid>signup-aws</guid><pubDate>Mon, 20 Jun 2022 09:06:39 GMT</pubDate></item><item><title>Testing websockets with wscat</title><link>https://sergiodelamo.com/blog/wscat.html</link><description><![CDATA[<p>The wscat utility is a convenient tool for testing a WebSocket API</p><pre><code>$ wscat -c ws://websocket-echo.comConnected (press CTRL+C to quit)&gt; hi there&lt; hi there&gt; are you a happy parrot?&lt; are you a happy parrot?</code></pre><p><a href="https://github.com/websockets/wscat">Go to the linked site</a></p>]]></description><guid>wscat</guid><pubDate>Mon, 06 Jun 2022 15:40:09 GMT</pubDate></item><item><title>The Code Review Pyramid</title><link>https://sergiodelamo.com/blog/code-review-pyramid.html</link><description><![CDATA[<p>Great visualization by Gunnar Morling about the Code Review Pyramid</p><blockquote><p>When it comes to code reviews, it’s a common phenomenon that there is much focus and long-winded discussions around mundane aspects like code formatting and style, whereas important aspects (does the code change do what it is supposed to do, is it performant, is it backwards-compatible for existing clients, and many others) tend to get less attention.</p></blockquote><p><img src="https://www.morling.dev/images/code_review_pyramid.svg" alt="" /></p><p><a href="https://www.morling.dev/blog/the-code-review-pyramid/">Go to the linked site</a></p>]]></description><guid>code-review-pyramid</guid><pubDate>Mon, 06 Jun 2022 14:16:29 GMT</pubDate></item><item><title>Geecon 2022 - Getting Started with the Micronaut® Framework</title><link>https://sergiodelamo.com/blog/introduction-to-the-micronaut-framework-geecon-2022-slides.html</link><description><![CDATA[<p><a href="https://www.youtube.com/watch?v=ibf8T1hoXMM">Go to the linked site</a></p><p><a href="https://www.youtube.com/watch?v=ibf8T1hoXMM">Video</a><br/><a href="https://images.sergiodelamo.com/getting_started_with_micronaut_geecon_2022.pdf">Slides</a>></p>]]></description><guid>introduction-to-the-micronaut-framework-geecon-2022-slides</guid><pubDate>Wed, 11 May 2022 14:13:07 GMT</pubDate></item><item><title>Talk at Geecon 2022 - Getting Started with the Micronaut® Framework</title><link>https://sergiodelamo.com/blog/introduction-to-the-micronaut-framework-geecon-2022.html</link><description><![CDATA[<p>I will be talking at Warsaw for GeeCon 2022.</p><blockquote><p>In this session, Sergio del Amo introduces the Micronaut® framework and demonstrates how the Framework's unique compile-time approach enables the development of ultra-lightweight Java applications. Compelling aspects of the Micronaut framework include: Develop applications with Java, Kotlin, or Apache Groovy Sub-second startup time Small processes that can run in as little as 10 MB of JVM heap No runtime reflection Dependency injection and AOP Reflection-free serialization A database access toolkit that uses ahead-of-time (AoT) compilation to pre-compute queries for repository interfaces. Cloud-native features Sergio also demonstrates how you can generate GraalVM native images of your Micronaut applications to achieve instant startup and ultra-low memory footprint.</p></blockquote><p>In you are in Warsaw for <a href="https://2022.geecon.org/schedule/">GeeCon 2022</a> say hello 👋🏻.</p><p><a href="https://2022.geecon.org/schedule/">Go to the linked site</a></p>]]></description><guid>introduction-to-the-micronaut-framework-geecon-2022</guid><pubDate>Tue, 03 May 2022 17:28:45 GMT</pubDate></item><item><title>Free Course at AWS Dublin office for AWS Lambda and Micronaut</title><link>https://sergiodelamo.com/blog/micronaut-aws-lambda-course-aws-dublin-may-2022.html</link><description><![CDATA[<p>I am excited to be part of this in-person event. It is an excellent opportunity to showcase Micronaut integration with AWS Lambda.</p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">🥳 Free <a href="https://twitter.com/micronautfw?ref_src=twsrc%5Etfw">@micronautfw</a> training! AWS will be teaming up with <a href="https://twitter.com/sdelamo?ref_src=twsrc%5Etfw">@sdelamo</a> to deliver a free in-person course in Dublin on Friday 27th May. <br>Covering:<br>🔥 Micronaut &amp; <a href="https://twitter.com/hashtag/AWSLambda?src=hash&amp;ref_src=twsrc%5Etfw">#AWSLambda</a><br>🔥 GraalVM<br>🔥 S3, API GW, DDB integrations<br><br>Limited availability, please send me a DM if you&#39;re interested.</p>&mdash; Mark Sailes (@MarkSailes3) <a href="https://twitter.com/MarkSailes3/status/1521126979818295296?ref_src=twsrc%5Etfw">May 2, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>]]></description><guid>micronaut-aws-lambda-course-aws-dublin-may-2022</guid><pubDate>Tue, 03 May 2022 17:28:45 GMT</pubDate></item><item><title>Micronaut® Framework and Microstream Java-native persistence engine</title><link>https://sergiodelamo.com/blog/presentation-micronaut-microstream.html</link><description><![CDATA[<p>I did a similar presentation at<a href="/blog/jconworld-2023-micronaut-eclipsestore.html">JCON World 2023</a>, and<a href="/blog/jcon-2023-micronaut-and-microstream.html">JCON 2023</a>.</p><p><a href="https://sergiodelamo.com">Sergio del Amo</a> introduces <a href="https://micronaut-projects.github.io/micronaut-microstream/snapshot/guide/">Microstream integration</a> with the Micronaut® Framework in this session.</p><p><a href="https://microstream.one">Microstream</a> is an Object-Graph Persistence solution that allows ultra-fast in-memory data processing with pure Java.</p><blockquote><ul><li>MicroStream is a Java-native object graph persistence engine for storing any complex Java object graph or any single subgraph and restoring it in RAM at any time by using a fundamentally new serialization concept designed from scratch.</li><li>With MicroStream, you can restore in RAM on demand the entire object graph, partial subgraphs, or only single objects.</li><li>Beyond serialization, MicroStream is ACID transaction safe, can handle your class changes and provides a garbage collector for the storage, multi-threaded IO, and connectors for various data storages.</li></ul></blockquote><p>This session will teach you how to use Microstream as your persistence engine in a Micronaut Application. Micronaut Microstream integration allows you to run several Microstream instances in the sample application, simplifies configuration, storage operations, metrics, health, etc.</p><p>Moreover, you can use Microstream as a cache implementation for the Micronaut Framework.</p><p>Attendees will discover how the Micronaut Framework and Microstream are a perfect combination to build ultra-fast applications without leaving the confines of Java.</p><h2>Target Attendees</h2><p>Everyone is welcome. However, we recommend attendees have at least a working familiarity with web development, HTTP, Java, and JVM development frameworks. Experience with Micronaut is a plus, but not required.</p><h2>Technical Requirements</h2><p>For the lab exercises, you will need JDK 11 and IntelliJ IDEA Community Edition or Ultimate.</p>]]></description><guid>presentation-micronaut-microstream</guid><pubDate>Sun, 17 Apr 2022 16:28:31 GMT</pubDate></item><item><title>Download Youtube Premium Invoices</title><link>https://sergiodelamo.com/blog/google-pay.html</link><description><![CDATA[<p>If you are subscribed to Youtube Premium or Google Drive, you can download an invoice by visiting <a href="https://pay.google.com/gp/w/u/0/home/activity">https://pay.google.com/</a></p><p><a href="https://pay.google.com/gp/w/u/0/home/activity">Go to the linked site</a></p>]]></description><guid>google-pay</guid><pubDate>Thu, 07 Apr 2022 05:25:27 GMT</pubDate></item><item><title>Micronaut Security Workshop at JBCNConf 2022</title><link>https://sergiodelamo.com/blog/jdbcconf-2022-micronaut-security.html</link><description><![CDATA[<p>On July 20th, I will teach a <a href="https://www.jbcnconf.com/2022/infoTalk.html?id=6229f5eb56274e0bddf007a2">workshop about Micronaut Security</a> at <a href="https://www.jbcnconf.com/2022">JBCNConf 2022</a> in Barcelona.</p><p><img src="https://images.sergiodelamo.com/logo-jbcnconf-sergio-delamo-micronaut-security-workshop.jpg" alt="JBCNConf Logo" /></p><p>Also, <a href="https://twitter.com/graemerocher">Graeme Rocher</a> will talk about <a href="https://www.jbcnconf.com/2022/infoTalk.html?id=6229173756274e0bddf0079d">Building Database applications with Micronaut Data and GraalVM</a> at JBCNConf 2022.</p><p><a href="https://www.jbcnconf.com/2022/infoTalk.html?id=6229f5eb56274e0bddf007a2">Go to the linked site</a></p>]]></description><guid>jdbcconf-2022-micronaut-security</guid><pubDate>Mon, 21 Mar 2022 09:19:36 GMT</pubDate></item><item><title>Micronaut Podcast E011</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e011.html</link><description><![CDATA[<p>I talked to Sakis Kaliakoudas about how they use the Micronaut framework, AWS Lambda, and Kotlin to develop Caribou.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/011.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e011</guid><pubDate>Mon, 14 Mar 2022 17:20:01 GMT</pubDate></item><item><title>Micronaut Podcast E010</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e010.html</link><description><![CDATA[<p>I talked about Micronaut Email.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/010.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e010</guid><pubDate>Mon, 28 Feb 2022 17:20:01 GMT</pubDate></item><item><title>Micronaut Podcast E009</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e009.html</link><description><![CDATA[<p>I talked with the Agorapulse crew. Agorapulse was a pioneer in the usage of Micronaut features from Grails Application. Moreover, they maintain several open-source libraries to help integrate Micronaut and Grails, and they have written about their migration from Grails to Micronaut.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/009.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e009</guid><pubDate>Mon, 14 Feb 2022 17:20:01 GMT</pubDate></item><item><title>Micronaut Podcast E008</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e008.html</link><description><![CDATA[<p>I talked about Micronaut Serialization with Graeme Rocher (Micronaut co-founder). Micronaut Serialization can serialize and deserialize Java types (including Java 17 records) to and from JSON and other formats without using reflection.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/008.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e008</guid><pubDate>Mon, 31 Jan 2022 17:20:01 GMT</pubDate></item><item><title>Micronaut Turbo - The return of the Monolith</title><link>https://sergiodelamo.com/blog/presentation-micronaut-turbo.html</link><description><![CDATA[<p>Similar presentation at <a href="https://speakerdeck.com/sdelamo/micronaut-turbo-return-of-the-monolith">JCON Prague</a>.</p><hr /><p>In this session, <a href="https://sergiodelamo.com">Sergio del Amo</a> introduces <a href="https://micronaut-projects.github.io/micronaut-views/latest/guide/#turbo">Turbo integration</a> with the Micronaut® Framework.</p><p><a href="https://turbo.hotwired.dev">Turbo</a> allows you to get the speed of a single-page web application without writing any JavaScript.</p><blockquote><p>Turbo bundles several techniques for creating fast, modern, progressively enhanced web applications without using much JavaScript. It offers a simpler alternative to the prevailing client-side frameworks, which put all the logic in the front-end and confine the server-side of your app to being little more than a JSON API.</p></blockquote><p>Topics include:</p><ul><li>Integrating Turbo in a Micronaut Application.</li><li>Use Micronaut Views Templates with Turbo Frames and Turbo Streams.</li><li>Respond Turbo Frames with the <code>@TurboFrameView</code> annotation.</li><li>Respond to form submissions using just HTML and a set of CRUD-like actions with Turbo Streams.</li><li>Deliver page changes with Turbo Streams over WebSockets.</li><li>Respond Turbo Streams with the <code>@TurboView</code> annotation.</li></ul><p>Micronaut Turbo brings back the joy of building web applications with JVM languages.</p><h2>Target Attendees</h2><p>Everyone is welcome. However, we recommend attendees have at least a working familiarity with web development, HTTP, Java, and JVM development frameworks. Experience with Micronaut is a plus, but not required.</p><h2>Technical Requirements</h2><p>For the lab exercises, you will need JDK 11 and IntelliJ IDEA Community Edition or Ultimate.</p>]]></description><guid>presentation-micronaut-turbo</guid><pubDate>Fri, 28 Jan 2022 16:28:31 GMT</pubDate></item><item><title>Security in the Micronaut® Framework</title><link>https://sergiodelamo.com/blog/presentation-micronaut-security.html</link><description><![CDATA[<p>Similar presentation at <a href="https://sergiodelamo.com/blog/gr8eu-2019-micronaut-security.html">GRConf 2019</a>.</p><hr /><p>In this session, <a href="https://sergiodelamo.com">Sergio del Amo</a> introduces <a href="https://micronaut-projects.github.io/micronaut-security/latest/guide/">Micronaut Security</a>, a fully-featured and customizable security solution for your applications.</p><p>Topics include:</p><ul><li>Authentication (Basic Auth, Cookie-based, Session-based, Bearer, LDAP, X.509)</li><li>Access authenticated user</li><li>Security Configuration (@Secured, Intercept URL map, and more)</li><li>OAuth 2.0 (Authorization Code, Client Credentials, Password Credentials grants)</li><li>OpenID Connect (integration with third-party OAuth 2.0 server providers: Auth0, Okta, Keycloak, Amazon Cognito)</li><li>JWT (signing, validation, generation, JWKS)</li><li>Token Propagation</li><li>Security Events</li></ul><p>Discover how easy it is to work with Micronaut Security and learn how to tailor it to your needs.</p><p>Sergio del Amo is a Micronaut core committer,  Developer Advocate for the Micronaut Foundation, and host of the <a href="https://micronautpodcast.com">Micronaut podcast</a>.</p><p>## Elevator Pitch</p><p>Every application needs security. In this talk, attendees discover how easy it is to work with Micronaut Security and learn how to tailor it to your needs.</p><h2>Note to Organizer</h2><p>This content can be a talk or a workshop.</p><h2>Target Attendees</h2><p>Everyone is welcome. However, we recommend attendees have at least a working familiarity with web development, HTTP, Java, and JVM development frameworks, such as Grails and Spring Boot. Experience with Micronaut is a plus, but not required.</p><h2>Technical Requirements</h2><p>For the lab exercises, you will need JDK 11 and IntelliJ IDEA Community Edition or Ultimate.</p>]]></description><guid>presentation-micronaut-security</guid><pubDate>Fri, 28 Jan 2022 16:28:31 GMT</pubDate></item><item><title>Getting Started with the Micronaut® Framework</title><link>https://sergiodelamo.com/blog/presentation-micronaut-introduction.html</link><description><![CDATA[<p>I did a similar presentation at<a href="https://www.youtube.com/watch?v=AR8AGGbkMcg">JDevSummit 2024</a>,<a href="https://www.youtube.com/watch?v=ibf8T1hoXMM&amp;list=PL9UkPxJVi4FT3gitylnECKe27Aar1c_Er">GeeCON 2022</a>,<a href="https://www.youtube.com/watch?v=-d-sOaGpg34&amp;t=18s">JCON-ONLINE 2022</a>,<a href="https://sergiodelamo.com/blog/micronaut-webinar-bucharest-2021-09-16.html">Bucharest JUG</a>,<a href="https://sergiodelamo.com/blog/jugmerida-intro-to-micronaut.html">Merida JUG (Spanish talk)</a>, and<a href="https://www.youtube.com/watch?v=vgVXRsIC3E8">Brighton Kotlin User Group</a>.</p><hr /><p>In this session, <a href="https://sergiodelamo.com">Sergio del Amo</a> introduces the <a href="https://micronaut.io">Micronaut® framework</a> and demonstrates how the Framework's unique compile-time approach enables the development of ultra-lightweight Java applications.</p><p>Compelling aspects of the Micronaut framework include:</p><ul><li>Develop applications with Java, Kotlin, or Apache Groovy</li><li>Sub-second startup time</li><li>Small processes that can run in as little as 10 MB of JVM heap</li><li>No runtime reflection</li><li>Dependency injection and AOP</li><li>Reflection-free serialization</li><li>A database access toolkit that uses ahead-of-time (AoT) compilation to pre-compute queries for repository interfaces.</li><li>Cloud-native features</li></ul><p>Sergio also demonstrates how you can generate GraalVM native images of your Micronaut applications to achieve instant startup and ultra-low memory footprint.</p><p>Sergio del Amo is a Micronaut core committer,  Developer Advocate for the Micronaut Foundation, and host of the <a href="https://micronautpodcast.com">Micronaut podcast</a>.</p><h1>Elevator Pitch</h1><p>This talk demonstrates how easy it is to get started with the <a href="https://micronaut.io">Micronaut Framework</a> through live-coding examples. Attendees will discover the most exciting features of the Framework and leave with the fundamentals necessary to begin building Micronaut applications.</p><h1>Talk Outline</h1><p>I deliver the talk with some slides introducing the framework, followed by a demo where I create a Micronaut Application and talk about some of the framework features; I leave some time at the end for Q&amp;A.</p>]]></description><guid>presentation-micronaut-introduction</guid><pubDate>Fri, 28 Jan 2022 16:28:31 GMT</pubDate></item><item><title>Micronaut® Framework and AWS Lambda</title><link>https://sergiodelamo.com/blog/presentation-micronaut-aws.html</link><description><![CDATA[<p>Similar presentation at <a href="https://www.youtube.com/watch?v=S3qYOVNaKS8&amp;t=195s">Barcelona JUG</a>, <a href="https://sergiodelamo.com/blog/combining-micronaut-framework-and-aws.html">Micronaut Framework and AWS Webinar</a>, and <a href="https://www.youtube.com/watch?v=PBvlwT27lKA">Micronaut AWS Lambda SnapStart integration</a>.</p><hr /><p>Can a Lambda function be fast if I use Java? Can I write my applications with a framework and still deploy them to AWS Lambda? Yes. It is possible with the Micronaut Framework.</p><p>Micronaut applications' characteristics, such as fast startup and low memory consumption, are perfect for Lambda, but the framework goes the extra mile to help you get fast cold startups with:</p><ul><li>GraalVM native image generation built-in in the Micronaut Gradle and Maven plugins and the integration with AWS Lambda custom runtimes.</li><li>Micronaut CRaC integration with AWS Lambda Snapstart. You can get amazingly fast cold startups while using Lambda's Java runtime.</li></ul><p>Additionally, you learn:</p><ul><li>How integrating Amazon API Gateway and AWS Lambda enables you to write your applications as you would with a Netty runtime (`@Controller's...) and run them in AWS Lambda.</li><li>Micronaut integration with Amazon Cloud Development Kit (CDK) to generate infrastructure as code. Set up easily a Serverless Architecture with Amazon API Gateway, AWS Lambda function, and DynamoDB table.</li><li>How to use reflection-free serialization in Lambda with Micronaut Serialisation.</li><li>How to integrate Amazon Cognito, Lambda, and Micronaut Security.</li><li>Different function styles depending on your Lambda Trigger.</li><li>How to develop and test your Lambda Functions.</li></ul><p>Attendees will gain an understanding of the vast Micronaut integration with AWS Lambda. They will be ready to start using Java with Lambda.</p><h2>Elevator Pitch</h2><p>Can a Lambda function be fast if I use Java? Can I write my applications with a framework and still deploy them to AWS Lambda? Yes. It is possible with the Micronaut Framework and its Lambda, GraalVM, and Snapstart integration.</p>]]></description><guid>presentation-micronaut-aws</guid><pubDate>Fri, 28 Jan 2022 16:28:31 GMT</pubDate></item><item><title>Micronaut Webinar at Manchester JUG</title><link>https://sergiodelamo.com/blog/micronaut-webinar-manchester-2022-03-17.html</link><description><![CDATA[<p>I did an online Micronaut talk at <a href="https://www.meetup.com/ManchesterUK-Java-Community/events/283472160/">Manchester Java User Group</a></p><p>📅 Dates: 2022, March 17⏳ Time: 1:30 PM to 2:30 PM CET📍 Location: Online.<br />👨🏻‍🏫 Instructor: Yours Truly.<br />💵 Registration Fee: Free</p><p><a href="https://www.youtube.com/watch?v=5UrKY3SbOkg">Go to the linked site</a></p>]]></description><guid>micronaut-webinar-manchester-2022-03-17</guid><pubDate>Fri, 28 Jan 2022 16:26:43 GMT</pubDate></item><item><title>Micronaut Podcast E007</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e007.html</link><description><![CDATA[<p>I talked with the Agorapulse crew. Agorapulse maintains several open-source Micronaut libraries. They talk about some of them (Snitch, Recurly, Rethrow, Segment, Newrelic, Worker, Console, Pierrot ...)..</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/007.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e007</guid><pubDate>Mon, 17 Jan 2022 17:20:01 GMT</pubDate></item><item><title>Promotion to Distinguished Engineer</title><link>https://sergiodelamo.com/blog/oci-distinguished-engineer.html</link><description><![CDATA[<p>I got promoted to<a href="https://www.linkedin.com/posts/gmbremehr_congratulations-to-paul-king-sergio-del-activity-6877355370151518208-vqPf">Distinguished Engineer</a>.</p><blockquote><p>OCI recognizes individuals who have demonstrated subject matter expertise in one or more technology areas, and who serve as an organizational and community thought leader and champion, as “Distinguished Engineer”.</p></blockquote><p>A Distinguished engineer:</p><blockquote><ul><li>Demonstrates subject matter expertise in specific technology area(s)</li><li>Applies expertise to create novel approaches to problems</li><li>Stays current with evolutions in the specific technology area(s) and/or emerging related technologies</li><li>Are Capable of communicating, sharing, consulting, on the subject matter, serving as a consultant/leader within OCI and with our clients and community</li><li>Shares expertise with others both inside and outside of OCI in the forms of mentoring, screencasts, user group and conference presentations, and writing</li><li>Provides significant contributions to related open source software</li><li>Brings name recognition to OCI through involvement in user groups, conferences, standards bodies, or similar</li></ul></blockquote><p>Two colleagues, which admire a lot <a href="https://twitter.com/paulk_asert">Paul King</a> (Apache Groovy Development lead) and <a href="https://twitter.com/Schlogen">James Kleeh</a> (Micronaut Development Lead), received the same promotion. I am humbled to receive the same promotion as they did.</p>]]></description><guid>oci-distinguished-engineer</guid><pubDate>Mon, 10 Jan 2022 09:13:41 GMT</pubDate></item><item><title>Micronaut Podcast E006</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e006.html</link><description><![CDATA[<p>I talked to James Kleeh (Micronaut development lead) about Micronaut Security.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/006.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e006</guid><pubDate>Mon, 03 Jan 2022 17:20:01 GMT</pubDate></item><item><title>Micronaut Podcast E005</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e005.html</link><description><![CDATA[<p>I talked to Cédric Champeau (Micronaut core committer at Oracle Labs) about the Micronaut AOT module - a new module that generates build-time optimizations for Micronaut applications.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/005.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e005</guid><pubDate>Mon, 20 Dec 2021 17:20:01 GMT</pubDate></item><item><title>Micronaut Podcast E004</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e004.html</link><description><![CDATA[<p>I talked to Graeme Rocher (Micronaut co-founder). Micronaut Data is a database access toolkit that uses Ahead of Time (AoT) compilation to pre-compute queries for repository interfaces that are then executed by a thin, lightweight runtime layer.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/004.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e004</guid><pubDate>Mon, 06 Dec 2021 17:20:01 GMT</pubDate></item><item><title>Audio Hijack settings for Codigobot</title><link>https://sergiodelamo.com/blog/audio-hijack-settings-for-podcast-skype-recording.html</link><description><![CDATA[<p>To record <a href="https://codigobot.com">CodigoBot Podcast</a>, we use <a href="https://rogueamoeba.com/audiohijack/">Audio Hijack</a>.</p><p>Create a new Audio Hijack session.</p><ul><li>Configure</li><li>New Session</li></ul><p>You need to create something such as:</p><p><img src="https://images.sergiodelamo.com/ahoverview.png" alt="" /></p><h2>Your Configuration:</h2><h3>Input Device</h3><p><img src="https://images.sergiodelamo.com/ahyourinput.png" alt="" /></p><p>Configure your Audio Device (for my that is Scarlett Solo USB). Each audio interface that you have, has potentially multiple channel. I selected both channels to be 1.</p><h3>Recorder</h3><p><img src="https://images.sergiodelamo.com/ahyourrecorder.png" alt="" /></p><p>Format the file is actually saved in.</p><p>File:  I selected <code>Código Bot - Sergio - Date Time</code> You should use your name instead of <code>Sergio</code>.</p><p>Save To: I selected the path to <code>raw</code> in <code>media.codigobot.com</code>. For example: <code>/Users/sdelamo/github/codigobot/media.codigobot.com/raw</code></p><p>Quality: High quality MP3</p><h4>Advanced Recording Options</h4><p>Format MP3<br />Bit Rate: 256 Kbps<br />Bit Rate Mode: Constant Bitrate<br />Sample Rate: 48000 Hz<br />Channels: Mono</p><h2>Guest Configuration</h2><h3>Skype</h3><p><img src="https://images.sergiodelamo.com/ahskype.png" alt="" /></p><p>Application Source: Skype</p><p>Include audio input: no<br />Fill playback gaps with silence: yes<br />Limit audio capture: no</p><h2>Output Device</h2><p>Add an output device. select as Audio Device your headphones to you are able to hear your guest.</p><p><img src="https://images.sergiodelamo.com/ahoutput.png" alt="" /></p><h2>Recorder</h2><p><img src="https://images.sergiodelamo.com/ahguestrecorder.png" alt="" /></p><p>File name: <code>Código Bot - Kini - Date Time</code> You should use your guest name instead of <code>Kini</code>.</p><h4>Advanced Recording Options</h4><p>Format: MP3<br />Bit Rate: 256 Kbps<br />Bit Rate Mode: Constant Bitrate<br />Sample Rate: 48000 Hz<br />Channels: Mono</p>]]></description><guid>audio-hijack-settings-for-podcast-skype-recording</guid><pubDate>Mon, 29 Nov 2021 09:36:09 GMT</pubDate></item><item><title>Micronaut Live - E009</title><link>https://sergiodelamo.com/blog/micronaut-live-day-e009.html</link><description><![CDATA[<p>In the 9th session of <a href="https://sergiodelamo.com/blog/micronaut-live-introduction.html">Micronaut Live</a>:</p><ul><li>We integrated <a href="https://www.liquibase.com/">Liquibase</a>, a Schema Migration Tool.</li><li>We changed from H2 to PostgreSQL and used <a href="https://www.testcontainers.org/">TestContainers</a> to use the same DB for the tests.</li><li>We used Swagger annotations to improve the generated OpenAPI documentation.</li></ul><p>Checkout the <a href="https://github.com/micronaut-advocacy/micronaut-live-newsletter/">Github repository</a> which contains the application's code.</p><p>For another Micronaut Live session, <a href="https://twitch.tv/micronautfw">tune in to twitch</a> Tuesdays and Thursdays at 10:30 a.m. CST / 17:30 CET to continue building a Micronaut Application.</p><p><a href="https://www.youtube.com/watch?v=9caw3p3Ucfg">Go to the linked site</a></p>]]></description><guid>micronaut-live-day-e009</guid><pubDate>Thu, 25 Nov 2021 13:26:16 GMT</pubDate></item><item><title>Micronaut Podcast E003 - Schema Migration Tools</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e003.html</link><description><![CDATA[<p>I talked to Iván López (Micronaut core commiter) about Micronaut integration with Flyway and Liquibase.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/003.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e003</guid><pubDate>Mon, 22 Nov 2021 17:20:01 GMT</pubDate></item><item><title>Disable transaction wrapping with @MicronautTest</title><link>https://sergiodelamo.com/blog/micronaut-test-transactional-false.html</link><description><![CDATA[<p><a href="https://micronaut-projects.github.io/micronaut-test/latest/guide/">Micronaut Test</a>:</p><blockquote><p>By default, when using <code>@MicronautTest</code>, each <code>@Test</code> method will be wrapped in a transaction that will be rolled back when the test finishes. This behaviour can be changed by using the transactional and rollback properties.</p></blockquote><p>For example, if you create an application with <code>data-jdbc</code>, <code>postgres</code> and <code>test-containers</code> features with <a href="https://launch.micronaut.io">Micronaut Launch</a>.</p><p>With an entity:</p><pre><code class="language-java">package com.example;import io.micronaut.core.annotation.NonNull;import io.micronaut.data.annotation.Id;import io.micronaut.data.annotation.MappedEntity;import javax.validation.constraints.NotBlank;@MappedEntitypublic class Book {    @Id    @NonNull    @NotBlank    private final String isbn;    @NonNull    @NotBlank    private final String name;    public Book(@NonNull String isbn,                @NonNull String name) {        this.isbn = isbn;        this.name = name;    }    @NonNull    public String getIsbn() {        return isbn;    }    @NonNull    public String getName() {        return name;    }}</code></pre><p>A JDBC repository:</p><pre><code class="language-java">package com.example;import io.micronaut.data.jdbc.annotation.JdbcRepository;import io.micronaut.data.model.query.builder.sql.Dialect;import io.micronaut.data.repository.CrudRepository;@JdbcRepository(dialect = Dialect.POSTGRES)public interface BookRepository extends CrudRepository&lt;Book, String&gt; {}</code></pre><p>And a Controller:</p><pre><code class="language-java">package com.example;import io.micronaut.http.MediaType;import io.micronaut.http.annotation.Controller;import io.micronaut.http.annotation.Get;import io.micronaut.http.annotation.Produces;@Controller(&quot;/books&quot;)class BookController {    private final BookRepository bookRepository;    BookController(BookRepository bookRepository) {        this.bookRepository = bookRepository;    }    @Produces(MediaType.TEXT_PLAIN)    @Get(&quot;/count&quot;)    Number count() {        return bookRepository.count();    }}</code></pre><p>The following test passes only if you set <code>transactional=false</code> with <code>@MicronautTest</code>.</p><pre><code class="language-java">package com.example;import io.micronaut.http.HttpRequest;import io.micronaut.http.MediaType;import io.micronaut.http.client.BlockingHttpClient;import io.micronaut.http.client.HttpClient;import io.micronaut.http.client.annotation.Client;import io.micronaut.test.extensions.junit5.annotation.MicronautTest;import jakarta.inject.Inject;import org.junit.jupiter.api.Test;import static org.junit.jupiter.api.Assertions.assertEquals;@MicronautTest(transactional = false)public class BookControllerTest {    @Inject    @Client(&quot;/&quot;)    HttpClient httpClient;    @Inject    BookRepository bookRepository;    @Test    void bookCount() {        String title = &quot;Building Microservices&quot;;        String isbn = &quot;1491950358&quot;;        Book book = bookRepository.save(new Book(isbn, title));        BlockingHttpClient client = httpClient.toBlocking();        HttpRequest&lt;?&gt; request = HttpRequest.GET(&quot;/books/count&quot;)                .accept(MediaType.TEXT_PLAIN);        Integer count = client.retrieve(request, Integer.class);        assertEquals(1, count);        bookRepository.delete(book);    }}</code></pre>]]></description><guid>micronaut-test-transactional-false</guid><pubDate>Mon, 22 Nov 2021 12:23:15 GMT</pubDate></item><item><title>Micronaut Live - E008</title><link>https://sergiodelamo.com/blog/micronaut-live-day-e008.html</link><description><![CDATA[<p>In the 8th session of <a href="https://sergiodelamo.com/blog/micronaut-live-introduction.html">Micronaut Live</a>:</p><ul><li>We used <a href="https://sendgrid.com">SendGrid</a> to send transactional emails.</li><li>We created an endpoint to send an email to every subscriber.</li><li>We added a feature to add unsubscribe links to emails.</li></ul><p>Code: https://github.com/micronaut-advocacy/micronaut-live-newsletter</p><p>And, much more!.</p><p>Checkout the <a href="https://github.com/micronaut-advocacy/micronaut-live-newsletter/">Github repository</a> which contains the application's code.</p><p>For another Micronaut Live session, <a href="https://twitch.tv/micronautfw">tune in to twitch</a> Tuesdays and Thursdays at 10:30 a.m. CST / 17:30 CET to continue building a Micronaut Application.</p><p><a href="https://www.youtube.com/watch?v=CJ1RM73x-iU">Go to the linked site</a></p>]]></description><guid>micronaut-live-day-e008</guid><pubDate>Mon, 22 Nov 2021 10:23:41 GMT</pubDate></item><item><title>Micronaut Live - E007</title><link>https://sergiodelamo.com/blog/micronaut-live-day-e007.html</link><description><![CDATA[<p>In the 8th session of <a href="https://sergiodelamo.com/blog/micronaut-live-introduction.html">Micronaut Live</a>:</p><ul><li>We used <a href="https://sendgrid.com">SendGrid</a> to send transactional emails.</li><li>We created an endpoint to send an email to every subscriber.</li><li>We added a feature to add unsubscribe links to emails.</li></ul><p>Code: https://github.com/micronaut-advocacy/micronaut-live-newsletter</p><p>And, much more!.</p><p>Checkout the <a href="https://github.com/micronaut-advocacy/micronaut-live-newsletter/">Github repository</a> which contains the application's code.</p><p>For another Micronaut Live session, <a href="https://twitch.tv/micronautfw">tune in to twitch</a> Tuesdays and Thursdays at 10:30 a.m. CST / 17:30 CET to continue building a Micronaut Application.</p><p><a href="https://www.youtube.com/watch?v=WL7DsrjYHNo">Go to the linked site</a></p>]]></description><guid>micronaut-live-day-e007</guid><pubDate>Mon, 22 Nov 2021 09:23:41 GMT</pubDate></item><item><title>100 bytes of CSS to look great everywhere</title><link>https://sergiodelamo.com/blog/100_bytes_of_css_to_look_great_everywhere.html</link><description><![CDATA[<p>Minimal CSS snippet to make an HTML page look good.</p><p>No wonder that <a href="https://twitter.com/swyx/status/1449472712720601088">@swyx's tweet</a> got more than 1500 likes. This is the snippet (visit the linked website for detailed explanation) :</p><pre><code class="language-css">html {	  max-width: 70ch;	  padding: 3em 1em;	  margin: auto;	  line-height: 1.75;	  font-size: 1.25em;}h1,h2,h3,h4,h5,h6 {	  margin: 3em 0 1em;}	p,ul,ol { margin-bottom: 2em; color: #1d1d1d; font-family: sans-serif;}</code></pre><p><a href="https://dev.to/swyx/100-bytes-of-css-to-look-great-everywhere-19pd">Go to the linked site</a></p>]]></description><guid>100_bytes_of_css_to_look_great_everywhere</guid><pubDate>Fri, 12 Nov 2021 15:58:30 GMT</pubDate></item><item><title>Micronaut Live - E006</title><link>https://sergiodelamo.com/blog/micronaut-live-day-e006.html</link><description><![CDATA[<p>In the 6th session of <a href="https://sergiodelamo.com/blog/micronaut-live-introduction.html">Micronaut Live</a>:</p><ul><li>We integrate <a href="https://getbootstrap.com">Bootstrap</a> into the application.</li><li>We create a <a href="https://www.thymeleaf.org">Thymleaf</a> template to render alerts. - We do redirections between controllers.</li><li>We learn how to instruct the HTTP Client to avoid following redirections.</li><li>We learn how to handle form submissions.</li><li>We set <code>dev</code> as the Micronaut default environment.</li></ul><p>And, much more!.</p><p>Checkout the <a href="https://github.com/micronaut-advocacy/micronaut-live-newsletter/">Github repository</a> which contains the application's code.</p><p>For another Micronaut Live session, <a href="https://twitch.tv/micronautfw">tune in to twitch</a> Tuesdays and Thursdays at 10:30 a.m. CST / 17:30 CET to continue building a Micronaut Application.</p><p><a href="https://www.youtube.com/watch?v=UVAgnN_FIBo">Go to the linked site</a></p>]]></description><guid>micronaut-live-day-e006</guid><pubDate>Fri, 12 Nov 2021 10:18:43 GMT</pubDate></item><item><title>Micronaut Live - E005</title><link>https://sergiodelamo.com/blog/micronaut-live-day-e005.html</link><description><![CDATA[<p>In the 5th session of <a href="https://sergiodelamo.com/blog/micronaut-live-introduction.html">Micronaut Live</a>, I integrate <a href="https://docs.micronaut.io/latest/guide/#contextEvents">Micronaut Context Events</a> to the application. We trigger an event when a user subscribes to the newsletter and we use the <code>@Async</code> annotation to process it asynchronously.</p><p>Checkout the <a href="https://github.com/micronaut-advocacy/micronaut-live-newsletter/">Github repository</a> which contains the application's code.</p><p>For another Micronaut Live session, <a href="https://twitch.tv/micronautfw">tune in to twitch</a> tomorrow at 10:30 a.m. CDT / 17:30 CET to continue building a Micronaut Application.</p><p><a href="https://www.youtube.com/watch?v=A9MaosWz7ns">Go to the linked site</a></p>]]></description><guid>micronaut-live-day-e005</guid><pubDate>Wed, 10 Nov 2021 17:05:20 GMT</pubDate></item><item><title>Micronaut Podcast E002</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e002.html</link><description><![CDATA[<p>I talked to <a href="https://twitter.com/schlogen">James Kleeh</a>, Micronaut development lead, about Micronaut versioning, repository branching, the usage of <a href="https://twitter.com/testcontainers">Testcontainers</a> in Micronaut testing.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/002.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e002</guid><pubDate>Mon, 08 Nov 2021 17:20:01 GMT</pubDate></item><item><title>Micronaut Live - E004</title><link>https://sergiodelamo.com/blog/micronaut-live-day-e004.html</link><description><![CDATA[<p>In the 4th session of <a href="https://sergiodelamo.com/blog/micronaut-live-introduction.html">Micronaut Live</a>, I integrate <a href="https://micronaut-projects.github.io/micronaut-views/latest/guide/">Micronaut Views</a> to the application. We render HTML on the server side to display a confirmation page when a user unsubscribes.</p><p>Checkout the <a href="https://github.com/micronaut-advocacy/micronaut-live-newsletter/">Github repository</a> which contains the application's code.</p><p>For another Micronaut Live session, <a href="https://twitch.tv/micronautfw">tune in to twitch</a> tomorrow at 10:30 a.m. CDT / 17:30 CET to continue building a Micronaut Application.</p><p><a href="https://www.youtube.com/watch?v=KkxZN9c8k7s">Go to the linked site</a></p>]]></description><guid>micronaut-live-day-e004</guid><pubDate>Mon, 08 Nov 2021 14:10:39 GMT</pubDate></item><item><title>Micronaut Live - E003</title><link>https://sergiodelamo.com/blog/micronaut-live-day-e003.html</link><description><![CDATA[<p>In the 3rd session of <a href="https://sergiodelamo.com/blog/micronaut-live-introduction.html">Micronaut Live</a>, I integrate <a href="https://micronaut-projects.github.io/micronaut-security/latest/guide/#jwt">Micronaut Security JWT</a> to the application. To confirm the newsletter subscription, the application will send a signed JWT with the user's email in the token claims.</p><p>Checkout the <a href="https://github.com/micronaut-advocacy/micronaut-live-newsletter/">Github repository</a> which contains the application's code.</p><p>For another Micronaut Live session, <a href="https://twitch.tv/micronautfw">tune in to twitch</a> tomorrow at 10:30 a.m. CDT / 17:30 CET to continue building a Micronaut Application.</p><p><a href="https://www.youtube.com/watch?v=q7ojCeBkIjE">Go to the linked site</a></p>]]></description><guid>micronaut-live-day-e003</guid><pubDate>Wed, 03 Nov 2021 13:59:09 GMT</pubDate></item><item><title>Micronaut Live - E002</title><link>https://sergiodelamo.com/blog/micronaut-live-day-e002.html</link><description><![CDATA[<p>In the 2nd session of <a href="https://sergiodelamo.com/blog/micronaut-live-introduction.html">Micronaut Live</a>, I integrate <a href="https://micronaut-projects.github.io/micronaut-data/latest/guide/#dbc">Micronaut Data JDBC</a> to the application.</p><p>Checkout the <a href="https://github.com/micronaut-advocacy/micronaut-live-newsletter/">Github repository</a> which contains the application's code.</p><p>For another Micronaut Live session, <a href="https://twitch.tv/micronautfw">tune in to twitch</a> tomorrow at 10:30 a.m. CDT / 17:30 CET to continue building a Micronaut Application.</p><p><a href="https://www.youtube.com/watch?v=yrjIMJHeFSs">Go to the linked site</a></p>]]></description><guid>micronaut-live-day-e002</guid><pubDate>Mon, 01 Nov 2021 12:04:23 GMT</pubDate></item><item><title>Micronaut Live - Day One</title><link>https://sergiodelamo.com/blog/micronaut-live-day-one.html</link><description><![CDATA[<p>The first day of <a href="https://sergiodelamo.com/blog/micronaut-live-introduction.html">Micronaut Live</a> was fun. We covered:</p><ul><li>Micronaut application generation with <a href="https://launch.micronaut.io">launch.micronaut.io</a></li><li>Github Repository creation</li><li>Continuous integration with Github Actions</li><li>DTOs Validation Testing</li><li>Health Check endpoint</li><li>Problem+JSON for Error Processing</li><li>Open API documentation generation.</li><li>Route HTTP Body payload validation.</li></ul><p>Checkout the <a href="https://github.com/micronaut-advocacy/micronaut-live-newsletter/">Github repository</a> which contains the application's code.</p><p>For another Micronaut Live session, <a href="https://twitch.tv/micronautfw">tune in to twitch</a> today at 10:30 a.m. CDT / 17:30 CET to continue building a Micronaut Application.</p><p><a href="https://www.youtube.com/watch?v=YpkxdUUq4Mo">Go to the linked site</a></p>]]></description><guid>micronaut-live-day-one</guid><pubDate>Fri, 29 Oct 2021 15:52:25 GMT</pubDate></item><item><title>Micronaut Live: Streaming today!</title><link>https://sergiodelamo.com/blog/micronaut-live-introduction.html</link><description><![CDATA[<p>Starting today at 10:30 US CDT/ 17:30 CET, I will be streaming at <a href="https://twitch.tv/micronautfw">twitch.tv/micronautfw</a>.</p><p>The goal is to stream twice a week and write a Micronaut application live with the help of the viewers. Hopefully, this stream becomes a place to gather and discuss how to code things with Micronaut.</p><p>Moreover, I want the outcome of the streams to be something useful, open-source that we all can use for our projects. For this initial session, I plan to write a newsletter subscribers repository. Join me!</p><p><a href="https://twitch.tv/micronautfw">Go to the linked site</a></p>]]></description><guid>micronaut-live-introduction</guid><pubDate>Tue, 26 Oct 2021 06:41:42 GMT</pubDate></item><item><title>Micronaut Podcast E001</title><link>https://sergiodelamo.com/blog/micronaut-podcast-e001.html</link><description><![CDATA[<p>I talked to Iván López (Micronaut core commiter) about Micronaut integration with GraalVM, the CI Iván built to ensure the Micronaut framework plays well with GraalVM and small tips for users who want to deploy GraalVM Native Images of their Micronaut applications to production.</p><p><img src="https://media.micronautpodcast.com/twitter-card.png" alt="" /></p><p><a href="https://micronautpodcast.com/001.html">Go to the linked site</a></p>]]></description><guid>micronaut-podcast-e001</guid><pubDate>Mon, 25 Oct 2021 17:20:01 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 020 - Developer Advocate and Community Builder</title><link>https://sergiodelamo.com/blog/codigobot020.html</link><description><![CDATA[<p>Hablo con <a href="https://kinisoftware.com">Kini</a> sobre sus últimas aventuras de Kini como <a href="https://aws.amazon.com/developer/community/community-builders/9">AWS Community Builder</a> y su papel en <a href="https://live.codemotion.com/devcast/">Codemotion Devcast</a> así como sobre mi nuevo trabajo de Sergio como <a href="https://sergiodelamo.com/blog/2gm-developer-advocate.html">Developer Advocate</a>.</p><p><a href="https://codigobot.com/020.html"><img src="https://media.codigobot.com/artwork-landscape.png" alt="" /></a></p><p><a href="https://codigobot.com/020.html">Go to the linked site</a></p>]]></description><guid>codigobot020</guid><pubDate>Fri, 22 Oct 2021 16:21:39 GMT</pubDate></item><item><title>Log Micronaut® Data queries</title><link>https://sergiodelamo.com/blog/micronaut-data-log-query.html</link><description><![CDATA[<p>I often add this logger to log my queries when using Micronaut Data.</p><p><em>src/main/resources/logback.xml</em></p><pre><code class="language-xml">    &lt;logger name=&quot;io.micronaut.data.query&quot; level=&quot;TRACE&quot;/&gt;&lt;/configuration&gt;</code></pre><p>Then, you will get an output such as:</p><pre><code class="language-bash">Executing SQL Insert: INSERT INTO `contact` (`name`,`job_title`,`id`) VALUES (?,?,?)Binding parameter at position 1 to value Tim Cook with data type: STRINGBinding parameter at position 2 to value CEO with data type: STRINGBinding parameter at position 3 to value Df6saLdgrlItF6IB9C6tU2r9Bvg with data type: STRING</code></pre><p>It is important to get visibility about the queries your application is executing.</p>]]></description><guid>micronaut-data-log-query</guid><pubDate>Thu, 21 Oct 2021 06:26:31 GMT</pubDate></item><item><title>Groovy Grape script to fetch a AWS Code Artifact token</title><link>https://sergiodelamo.com/blog/groovy-grape-and-aws-code-artifact.html</link><description><![CDATA[<p><a href="https://groovy-lang.org">Apache Groovy</a> has a great feature called <a href="https://docs.groovy-lang.org/latest/html/documentation/grape.html">Grape</a>:</p><blockquote><p>Grape is a JAR dependency manager embedded into Groovy. Grape lets you quickly add maven repository dependencies to your classpath, making scripting even easier. The simplest use is as simple as adding an annotation to your script:</p></blockquote><p>I recently needed a script to fetch a token to consume a <a href="https://aws.amazon.com/codeartifact/">AWS Code Artifact</a> repository and save it in a <code>local.properties</code> file.</p><h2>Script</h2><p>The following script shows how easy is to do that with a Groovy Script:</p><p><em>codeartifact.groovy</em></p><pre><code class="language-groovy">@Grab(group='software.amazon.awssdk', module='codeartifact', version='2.16.42')@Grab(group='software.amazon.awssdk', module='sts', version='2.16.42')import software.amazon.awssdk.auth.credentials.ProfileCredentialsProviderimport software.amazon.awssdk.regions.Regionimport software.amazon.awssdk.services.codeartifact.CodeartifactClientimport software.amazon.awssdk.services.codeartifact.CodeartifactClientBuilderimport software.amazon.awssdk.services.codeartifact.model.GetAuthorizationTokenRequestimport software.amazon.awssdk.services.codeartifact.model.GetAuthorizationTokenResponseString awsCodeArtifactRegion = 'eu-central-1'String awsCodeArtifactDomain = 'XXX'String awsCodeArtifactOwner = 'YYY'String awsCodeArtifactRepository = 'libs'class Token {    String token    Long expiration}class TokenRequest {    String owner    String domain    String profile    String region    Integer durationSeconds}class TokenFetcher {    static Token fetchToken(TokenRequest tokenRequest) {        CodeartifactClient client = instantiateClient(tokenRequest)        GetAuthorizationTokenRequest request = authorizationTokenRequest(tokenRequest)        GetAuthorizationTokenResponse rsp = client.getAuthorizationToken(request)        new Token(token: rsp.authorizationToken(), expiration: rsp.expiration().toEpochMilli())    }    private static GetAuthorizationTokenRequest authorizationTokenRequest(TokenRequest tokenRequest) {        GetAuthorizationTokenRequest.Builder tokenRequestBuilder = GetAuthorizationTokenRequest.builder()        if (tokenRequest.durationSeconds) {            tokenRequestBuilder = tokenRequestBuilder.durationSeconds(tokenRequest.durationSeconds)        }        tokenRequestBuilder.domain(tokenRequest.domain)        tokenRequestBuilder.domainOwner(tokenRequest.owner)        (GetAuthorizationTokenRequest) tokenRequestBuilder.build()    }    private static CodeartifactClient instantiateClient(TokenRequest tokenRequest) {        CodeartifactClientBuilder builder = CodeartifactClient.builder()        if (tokenRequest.region) {            builder = builder.region(Region.of(tokenRequest.region))        }        if (tokenRequest.profile) {            builder = builder.credentialsProvider(ProfileCredentialsProvider.create(tokenRequest.profile))        }        builder.build()    }}TokenRequest tokenRequest = new TokenRequest()tokenRequest.owner = awsCodeArtifactOwnertokenRequest.domain = awsCodeArtifactDomaintokenRequest.durationSeconds = 43200LtokenRequest.region = awsCodeArtifactRegionToken token = TokenFetcher.fetchToken(tokenRequest)println &quot;&quot;&quot;\{    &quot;token&quot;: &quot;${token.token}&quot;,    &quot;expiration&quot;: &quot;${token.expiration}&quot;,}&quot;&quot;&quot;Properties properties = new Properties()properties.setProperty(&quot;expiration&quot;,&quot;&quot; + token.expiration)properties.setProperty(&quot;codeartifactToken&quot;, token.token)File outputFile = new File('local.properties')if (!outputFile.exists()) {    outputFile.createNewFile()}properties.store(outputFile.newOutputStream(), &quot;&quot;)println &quot;saved token to local.properties&quot;</code></pre><h2>Run</h2><p>To execute the script:</p><ul><li>Edit <code>codeartifact.groovy</code> and enter your AWS Code Artifact region, domain, owner and lib parameters at the top of the file.</li><li>Run <code>aws configure</code> and authenticate with a user with IAM access to AWS Code Artifact.</li><li>Run <code>groovy codeartifact.groovy</code> and obtain a token.</li></ul><p>We needed a script instead of using the AWS CLI always, because in some environments we did not have the AWS CLI installed and we only had a AWS Secret Key and Access key ID exposed as environment variables.</p>]]></description><guid>groovy-grape-and-aws-code-artifact</guid><pubDate>Mon, 18 Oct 2021 14:17:28 GMT</pubDate></item><item><title>21 Apps for 2021 - The Clock</title><link>https://sergiodelamo.com/blog/macos_the_clock.html</link><description><![CDATA[<p><a href="https://apps.apple.com/us/app/the-clock/id4887645459">The Clock</a> is a beautiful MacOS application. Moreover, if you work with a team in multiple time zones, this is the best clock app in MacOS.</p><h2>Time zones</h2><ul><li><p><a href="https://twitter.com/puneetbhl">Puneet Behl</a>, Grails framework Development Lead, lives closes to Dehli, India.</p></li><li><p>Most of my <a href="https://objectcomputing.com">Object Computing</a>  colleagues are in St. Louis, USA.</p></li><li><p><a href="https://twitter.com/paulk_asert">Paul King</a>, <a href="https://groovy-lang.org">Apache Groovy</a> extraordinaire, lives in Brisbane,  Australia. The other side of the globe from where I live.</p></li><li><p>I often support clients in the UK.</p></li></ul><p>The time zone slider is the killer feature for me:</p><p><img src="https://images.sergiodelamo.com/clock-slider.gif" alt="Clock Slider" /></p><h2>The Clock Menu bar options</h2><p>I use <em>The Clock</em> only to show the time in the menu bar:</p><p><img src="https://images.sergiodelamo.com/the-clock-menu-bar-options.png" alt="The Clock menu bar options" /></p><h2>Default MacOS Clock Options</h2><p>I change the default Menu Bar's Clock to <code>Analogue</code>:</p><p><img src="https://images.sergiodelamo.com/menu-bar-clock-analgoue.png" alt="Clock Slider" /></p><p>You cannot hide it because clicking on it shows notifications and widgets. It should be possible to show a notification icon instead of a clock to show notifications. That it is bad design to me.</p><h2>Fantastical Menu bar options</h2><p>I use <a href="https://flexibits.com/fantastical">Fantastical</a> for my calendaring. I show the date in the menu bar:</p><p><img src="https://images.sergiodelamo.com/fantastical-menu-bar-shows-date.png" alt="Fantastical menu bar show date" /></p><h2>End result</h2><p>This is how my menu bar time situation looks like:</p><p><img src="https://images.sergiodelamo.com/menu-bar-the-clock.png" alt="menu bar Fantastical, The Clock and default clock" /></p><p><a href="https://apps.apple.com/us/app/the-clock/id488764545">Go to the linked site</a></p>]]></description><guid>macos_the_clock</guid><pubDate>Sun, 17 Oct 2021 08:45:51 GMT</pubDate></item><item><title>Github activity CLI updated to Micronaut® framework 3.1</title><link>https://sergiodelamo.com/blog/githubactivity-cli-update-to-micronaut-three-one.html</link><description><![CDATA[<p>I have updated the <a href="https://github.com/sdelamo/githubactivity">Github Activity CLI</a>, the command line tool which I use to check my Github activity, to Micronaut® framework 3.1.</p><p>The update is simple:</p><ul><li><a href="https://github.com/sdelamo/githubactivity/commit/5a6f7bfc81740bb303e61c5861d822e98d95a43b">Update to 7.1.0</a> the <a href="https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow">Shadow Gradle Plugin</a>, a Gradle plugin for collapsing all dependencies and project code into a single Jar file.</li><li><a href="https://github.com/sdelamo/githubactivity/commit/a987614ee7a50a044b3d7a94ad3cfe1ccadeed49">Update to 2.0.6</a> the <a href="https://plugins.gradle.org/plugin/io.micronaut.application">Micronaut Application Gradle Plugin</a>.</li><li><a href="https://github.com/sdelamo/githubactivity/commit/2fe6568b7a9393cd1c97fd1df193e0fff90694d1">Set Micronaut version to 3.1.0</a>.</li><li><a href="https://github.com/sdelamo/githubactivity/commit/90b59952eee6b2ef3eb10f40f3575ba679b19c43">Bump up gradle to 7.2</a> with command <code>./gradlew wrapper --gradle-version=7.2</code></li></ul><p><a href="https://github.com/sdelamo/githubactivity">Go to the linked site</a></p>]]></description><guid>githubactivity-cli-update-to-micronaut-three-one</guid><pubDate>Sat, 16 Oct 2021 11:35:06 GMT</pubDate></item><item><title>&#x1f468;&#x1f3fb;‍&#x1f3eb; Teaching Micronaut Deep Dive</title><link>https://sergiodelamo.com/blog/micronaut-deep-dive-october-2021.html</link><description><![CDATA[<p>🗓 Dates: Oct. 18 to Oct. 21<br />⏰ Time: 9:00 a.m. to 12:00 p.m. CDT<br />📍 Location: Online<br />🧑‍🏫 Instructor: Yours truly<br />💵 Registration Fee: $199.00 USD</p><p><a href="https://objectcomputing.com/training/register/offering/298">Enroll Now</a></p><blockquote><p>The following topics are covered in this course.</p><ul><li>Introduction to the Micronaut® framework</li><li>Controllers</li><li>Compile-time dependency injection</li><li>Application configuration</li><li>Testing</li><li>HTTP client</li><li>Service discovery</li><li>Management endpoints</li><li>Static file resolution</li><li>Polyglot Micronaut</li><li>Micronaut Data</li><li>Introduction to serverless deployment</li><li>Stand-alone CLI apps</li></ul></blockquote><p>During the course, I teach by building an application and explaining the concepts. Don't expect slides but 12 hours of live coding.</p><p><a href="https://objectcomputing.com/services/training/catalog/micronaut-training/micronaut-deep-dive">Go to the linked site</a></p>]]></description><guid>micronaut-deep-dive-october-2021</guid><pubDate>Fri, 15 Oct 2021 06:41:12 GMT</pubDate></item><item><title>cURL command to get the exact number of stars in a Github Project</title><link>https://sergiodelamo.com/blog/exact-number-of-github-stars.html</link><description><![CDATA[<p>I am trying to collect some metrics to measure my <a href="https://sergiodelamo.com/blog/2gm-developer-advocate.html">advocacy job</a>.</p><p>The following <a href="https://curl.se/">cURL</a> command returns the number of <a href="https://docs.github.com/en/get-started/exploring-projects-on-github/saving-repositories-with-stars#about-stars">Github Stars</a> for <a href="https://github.com/micronaut-projects/micronaut-core">micronaut-core repository</a>.</p><pre><code class="language-bash">curl https://api.github.com/repos/micronaut-projects/micronaut-core | grep 'stargazers_count' </code></pre>]]></description><guid>exact-number-of-github-stars</guid><pubDate>Thu, 14 Oct 2021 09:37:25 GMT</pubDate></item><item><title>Micronaut Job Opportunity</title><link>https://sergiodelamo.com/blog/work-on-open-source-micronaut-grails-groovy.html</link><description><![CDATA[<p>We have an <a href="https://jobs.lever.co/oci/ead05337-ea10-46c7-88bc-fff2f4f0e407">opening in our open-source (OSS) team</a> at <a href="https://objectcomputing.com">Object Computing</a>. You could be contributing daily to the <a href="https://micronaut.io">Micronaut® framework</a> and help us reshape the JVM frameworks' space. Working in Open Source is challenging, but it is fulfilling.</p><p>It is remote-friendly. Indeed, all of us in the 2GM (Apache Groovy, Grails and Micronaut® frameworks) team are working remotely.</p><p>If you have any doubt, you can <a href="https://sergiodelamo.com/contact.html">reach out to me</a>.</p><p><a href="https://jobs.lever.co/oci/ead05337-ea10-46c7-88bc-fff2f4f0e407">Go to the linked site</a></p>]]></description><guid>work-on-open-source-micronaut-grails-groovy</guid><pubDate>Thu, 14 Oct 2021 08:50:59 GMT</pubDate></item><item><title>&#x1f5e3; Madrid GUG Ask me Anything</title><link>https://sergiodelamo.com/blog/madrid-gug-ama-2021.html</link><description><![CDATA[<p>I will be joining <a href="https://twitter.com/ilopmar">Iván López</a> tomorrow in a <em>Ask me Anything</em> meetup of <a href="https://www.madridgug.com">Madrid GUG (Grooy Users Group)</a>. We are both <a href="https://micronaut.io">Micronaut</a> committers and we are asking people to ask us anything. It feelspretentious but people are curious about Open Source and, hopefully, we have something interesting to say about it.</p><p>Click the <a href="https://www.meetup.com/madrid-gug/events/281291389/">Attend online</a> button in the Meetup page. It is free.</p><h2>Questions</h2><p>There is an <a href="https://bit.ly/micronaut-ama">online form</a> if you want to submit a question in advanced but you can submit questions during the event via the chat.</p><p>Madrid GUG's talks are usually in Spanish but you are welcome to ask questions in English if you want.</p><h2>Watch Live</h2><p>You can <a href="https://t.co/Vz8JdVF47R?amp=1">follow the meeting live via Youtube</a>.</p><h2>Madrid GUG</h2><p>Madrid GUG is my community. I used to drive to Madrid to attend the live meetings and it is nice to see the people in the group, even if this time is, through the computer screen.</p>]]></description><guid>madrid-gug-ama-2021</guid><pubDate>Wed, 13 Oct 2021 14:04:25 GMT</pubDate></item><item><title>AppleScript - Open Mail to an specific inbox</title><link>https://sergiodelamo.com/blog/apple-script-open-mail-to-an-inbox.html</link><description><![CDATA[<p>I use MacOS <a href="https://support.apple.com/guide/mail/welcome/mac">Mail</a> to handle multiple email accounts. I often want to open my work inbox and avoid seeing the other email.</p><p>The following AppleScript snippet opens my <a href="https://objectcomputing.com">OCI</a> inbox:</p><pre><code class="language-applescript">tell application &quot;Mail&quot;    activate    set ociInbox to mailbox &quot;INBOX&quot; of account &quot;OCI&quot;    tell message viewer 1            set selected mailboxes to ociInbox        end tellend tell</code></pre><p>I learned about this thanks to the webinar <em>Make Apple Mail dance</em> webinar by <a href="https://www.macsparky.com">David Sparks</a></p>]]></description><guid>apple-script-open-mail-to-an-inbox</guid><pubDate>Wed, 13 Oct 2021 10:28:20 GMT</pubDate></item><item><title>Pushover 1.1.0 Released!</title><link>https://sergiodelamo.com/blog/pushover-one-one-zero.html</link><description><![CDATA[<p>Micronaut Pushover now uses Micronaut 3.1.0</p><p><a href="https://github.com/sdelamo/pushover/commit/705632ff4590034985d56041490d70bfa7dab57e">Easy upgrade</a>. I hit <a href="https://github.com/micronaut-projects/micronaut-core/issues/6323">this issue</a> in one of the library tests.</p>]]></description><guid>pushover-one-one-zero</guid><pubDate>Wed, 13 Oct 2021 09:33:09 GMT</pubDate></item><item><title>Grails 5 released!</title><link>https://sergiodelamo.com/blog/grails-5-GA.html</link><description><![CDATA[<p>A new mayor release of the Grails framework.</p><p>A <a href="https://grails.org/blog/2021-10-11-grails-5-ga.html">new mayor release of the Grails framework</a>.</p><p>Grails stands on the shoulder of Giants. This release elevates the framework by updating those Giants.</p><blockquote><p>This release includes <a href="https://groovy-lang.org/releasenotes/groovy-3.0.html">Apache Groovy 3</a>, <a href="https://micronaut.io/2021/08/18/micronaut-framework-3-released/">Micronaut® framework 3</a>, <a href="https://docs.gradle.org/7.0/release-notes.html">Gradle 7</a>, <a href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.5-Release-Notes">Spring Boot 2.5</a>, <a href="https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-5.x#upgrading-to-version-53">Spring framework 5.3</a>, and <a href="https://spockframework.org/spock/docs/2.0/release_notes.html">Spock 2.0</a>.</p></blockquote><p>It should be seamless upgrade.</p><blockquote><p>For applications on Grails framework 4, it should be an easy upgrade, as there are not many API changes in Grails framework 5. Also, most Grails framework 4 plugins should work just fine, unless they are using specific Spring, Spring Boot, or Groovy APIs that have been changed or removed.</p></blockquote><p>Congratulations to <a href="https://twitter.com/puneetbhl">@puneetbhl</a>, <a href="https://twitter.com/Schlogen">@Schlogen</a>, <a href="https://twitter.com/davydotcom">@davydotcom</a> and the whole <a href="https://grails.org">Grails</a> Community.</p><p><a href="https://grails.org/blog/2021-10-11-grails-5-ga.html">Go to the linked site</a></p>]]></description><guid>grails-5-GA</guid><pubDate>Tue, 12 Oct 2021 16:46:22 GMT</pubDate></item><item><title>Micronaut Filter Regex</title><link>https://sergiodelamo.com/blog/micronaut-regex-filter.html</link><description><![CDATA[<p>Since Micronaut® framework 3.1, you can use a regular expression in the HTTP server filter patterns.</p><p>Micronaut <a href="https://docs.micronaut.io/latest/guide/#filters">HTTP Server filters</a> supported Ant-style path patterns. Since 3.1, they support also regular expressions. The default pattern style is Ant-style path pattern.</p><h2>Example</h2><p>Imagine you have an API, which returns a list of secrets. You use a url path variable to version your API. For example: <code>/api/v1/secrets</code>.</p><p>My first version was a bit naive, I did not even returned JSON, but plain text instead:</p><pre><code class="language-java">package com.example.api.v1;import io.micronaut.http.MediaType;import io.micronaut.http.annotation.Controller;import io.micronaut.http.annotation.Get;import io.micronaut.http.annotation.Produces;import java.util.Collections;import java.util.List;@Controller(&quot;/api/v1&quot;)class SecretController {    @Produces(MediaType.TEXT_PLAIN)    @Get(&quot;/secret&quot;)    List&lt;String&gt; index() {        return Collections.singletonList(&quot;I admire Moriarty&quot;);    }}</code></pre><p>for v2, I returned JSON:</p><pre><code class="language-java">package com.example.api.v2;import io.micronaut.http.annotation.Controller;import io.micronaut.http.annotation.Get;import java.util.Collections;import java.util.Map;@Controller(&quot;/api/v2&quot;)public class SecretController {    @Get(&quot;/secret&quot;)    Map&lt;String, Object&gt; index() {        return Collections.singletonMap(&quot;messages&quot;,             Collections.singletonList(&quot;I admire Moriarty&quot;));    }}</code></pre><p>For v3, I changed the JSON key from <code>messages</code> to <code>secrets</code>.</p><pre><code class="language-java">package com.example.api.v3;import io.micronaut.http.annotation.Controller;import io.micronaut.http.annotation.Get;import java.util.Collections;import java.util.Map;@Controller(&quot;/api/v3&quot;)public class SecretsController {    @Get(&quot;/secret&quot;)    Map&lt;String, Object&gt; index() {        return Collections.singletonMap(&quot;secrets&quot;,         Collections.singletonList(&quot;I admire Moriarty&quot;));    }}</code></pre><p>I want to keep <code>v1</code>, <code>v2</code> endpoints alive. However, I want to notify my consumers that they should migrate to <code>v3</code>.</p><p>I can add a <a href="https://datatracker.ietf.org/doc/html/draft-dalal-deprecation-header-03"><code>Deprecation</code> HTTP Header</a> to the HTTP Responses emitted by <code>v1</code> and <code>v2</code> endpoints. Thanks to the support of regular expressions since Micronaut Framework 3.1, it is really easy to do it:</p><pre><code class="language-java">package com.example;import io.micronaut.http.HttpRequest;import io.micronaut.http.MutableHttpResponse;import io.micronaut.http.annotation.Filter;import io.micronaut.http.filter.FilterPatternStyle;import io.micronaut.http.filter.HttpServerFilter;import io.micronaut.http.filter.ServerFilterChain;import org.reactivestreams.Publisher;import reactor.core.publisher.Flux;@Filter(patternStyle = FilterPatternStyle.REGEX,         patterns = &quot;/api/v(1|2)/.*&quot;)public class DeprecatedApiVersionsFilter implements HttpServerFilter {    @Override    public Publisher&lt;MutableHttpResponse&lt;?&gt;&gt; doFilter(HttpRequest&lt;?&gt; request, ServerFilterChain chain) {        return Flux.from(chain.proceed(request))                .map(response -&gt; response.header(&quot;Deprecation&quot;, &quot;true&quot;));    }}</code></pre><p>The key in the above <code>Filter</code> is that I am defining my pattern style to be a regular expression with <code>patternStyle = FilterPatternStyle.REGEX</code> and then I can use a regular expression: <code>/api/v(1|2)/.*</code> as the pattern value.</p><p><code>/api/v(1|2)/.*</code> matches:</p><p>✅ <code>/api/v1/secrets</code><br />✅ <code>/api/v2/secrets</code><br />⭕️ <code>/api/v3/secrets</code></p><p>Regular expressions is an art. To work with regular expressions I use <a href="https://krillapps.com/patterns/">Patterns for MacOS</a> or <a href="https://www.regexbuddy.com">RegexBuddy</a> in Windows.</p>]]></description><guid>micronaut-regex-filter</guid><pubDate>Mon, 11 Oct 2021 17:28:53 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 019 - Living Apps</title><link>https://sergiodelamo.com/blog/codigobot019.html</link><description><![CDATA[<p>Hablo con <a href="https://kinisoftware.com">Kini</a> sobre su día a día y desarrollando Living Apps en Telefónica.</p><p><a href="https://codigobot.com/019.html"><img src="https://media.codigobot.com/artwork-landscape.png" alt="" /></a></p><p><a href="https://codigobot.com/019.html">Go to the linked site</a></p>]]></description><guid>codigobot019</guid><pubDate>Sat, 09 Oct 2021 16:20:47 GMT</pubDate></item><item><title>Developer Advocate for Micronaut, Grails, Groovy</title><link>https://sergiodelamo.com/blog/2gm-developer-advocate.html</link><description><![CDATA[<p>Starting today, I transition to a newly formed Developer Advocate role for the 2GM communities at OCI</p><p>Starting today, I <a href="https://micronaut.io/2021/10/08/2gm-developer-advocate/">transition to a newly formed Developer Advocate role</a> for the 2GM (<a href="https://groovy-lang.org">Groovy</a> / <a href="https://grails.org">Grails</a> / <a href="https://micronaut.io">Micronaut</a>) communities at <a href="https://objectcomputing.com">Object Computing</a>. I will continue to work on engineering tasks. However, I will dedicate most of my time to showing what the frameworks are capable of.</p><p>Ten years ago, I discovered the Grails framework, and I've been fortunate to work for Object Computing, the home of the Grails and Micronaut® frameworks, for the past 5 years.</p><h2>Grails</h2><p>The <a href="https://grails.org">Grails</a> framework is a mature toolkit, and it continues to enhance developer productivity around the world. I hosted a private training last week for a team new to the technology. It is always fascinating to see developers' first reactions when they encounter the Grails framework's productivity perks for the first time. The Grails 5 release is imminent, and its integration with the Micronaut® framework creates even more exciting opportunities. Its convention-over-configuration approach, the use of Groovy, and the integration with beautiful tools such <a href="https://spockframework.org">Spock</a> and <a href="https://gebish.org">Geb</a> make the framework a highly productive option for many teams and applications.</p><h2>Micronaut</h2><p>I have been lucky to witness the birth of the <a href="https://micronaut.io">Micronaut</a> framework from its inception. I was there back when it was still named project Particle back in 2017, and I have made a lot of contributions to it, primarily in the <a href="https://github.com/micronaut-projects/micronaut-security">security</a>, <a href="https://github.com/micronaut-projects/micronaut-problem-json">problem-json</a>, <a href="https://github.com/micronaut-projects/micronaut-aws">aws</a>, <a href="https://github.com/micronaut-projects/micronaut-multitenancy">multitenancy</a>, <a href="https://github.com/micronaut-projects/micronaut-starter">starter</a> and <a href="https://github.com/micronaut-projects/micronaut-core">core</a> modules. However, I have barely scratched the surface! The framework has more than 40 modules. The Micronaut® framework's potential is immense. You can build any kind of application (function, messaging, server, client, cli) with your architecture of choice (microservices, monolith), your favorite build tool, and your beloved JVM programming language. I think that it is fair to say that, since the Micronaut® framework's appearance back in 2018, the JVM framework space has seen a resurgence. And the best is yet to come! I feel privileged to come along for the journey.</p><h2>Advocacy</h2><p>Developer advocacy has always pulled me like a magnet. Over the past five years, I have done <a href="https://sergiodelamo.com/blog/tag/talk.html">public speaking</a>, conducted training, and written <a href="https://groovycalamari.com">a weekly newsletter</a>. The thing I am most proud of during this five-year period is the creation of the <a href="https://guides.grails.org">Grails</a> and <a href="https://guides.micronaut.io">Micronaut Guides</a>, which walk users through some of the Frameworks' capabilities. As a writer and reviewer, I have been involved in almost every guide.</p><p>But, there are still many things that we can do to improve developer experience with the Frameworks, from people just discovering them to teams using them in their production applications.</p><p>I think this is what the frameworks need, and I have ideas about how we can support the community. This new role will allow me to do that. I'm excited about helping users unleash their productivity. The community will fulfill the rest of the deal by building amazing applications.</p><p>Sergio del Amo</p>]]></description><guid>2gm-developer-advocate</guid><pubDate>Sat, 09 Oct 2021 07:40:32 GMT</pubDate></item><item><title>Micronaut development environment</title><link>https://sergiodelamo.com/blog/application-dev-enviornment.html</link><description><![CDATA[<p>I use <a href="https://docs.micronaut.io/latest/guide/#environments">Micronaut Default Environment</a> to define a development environment.</p><p>I replace the <code>Application</code> class:</p><pre><code class="language-java">public class Application {    public static void main(String[] args) {        Micronaut.run(Application.class, args);   }}</code></pre><p>with:</p><pre><code class="language-java">public class Application {    public static void main(String[] args) {           Micronaut.build(args)                .mainClass(Application.class)                .defaultEnvironments(Environment.DEVELOPMENT)                .start();    }}</code></pre><p>Then, I add to <code>.gitignore</code> a configuration file for the <code>dev</code> environment.</p><pre><code>......src/main/resources/application-dev.yml</code></pre><p>What do I use <code>application-dev.yml</code> for? I have configuration for my local database, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies">disable cookie access restriction</a> while running in <code>localhost</code>, etc. Because of the git ignore inclusion, each developer in the team can have an <code>application-dev.yml</code> tailored to his machine.</p>]]></description><guid>application-dev-enviornment</guid><pubDate>Thu, 30 Sep 2021 08:45:38 GMT</pubDate></item><item><title>Micronaut Error Responses with Problem+JSON</title><link>https://sergiodelamo.com/blog/micronaut-problem-json.html</link><description><![CDATA[<p><a href="https://datatracker.ietf.org/doc/html/rfc7807">problem+json</a> is my preferred way of formatting API errors and it is easy to use it in a Micronaut Application.</p><p>A request to a non existing route:</p><pre><code class="language-bash">curl -i locahost:8080/api/v1/bogus</code></pre><p>returns:</p><pre><code class="language-json">{    &quot;message&quot;:&quot;Not Found&quot;,    &quot;_links&quot;:{&quot;self&quot;:{&quot;href&quot;:&quot;/api/v1/bogus&quot;,&quot;templated&quot;:false}},    &quot;_embedded&quot;:{&quot;errors&quot;:[{&quot;message&quot;:&quot;Page Not Found&quot;}]}}</code></pre><p>The previous payload is formatted with <a href="https://github.com/blongden/vnd.error">vnd.error</a>. The response content type is <code>application/json</code>.</p><p>If you add the dependency <code>io.micronaut.problem:micronaut-problem-json</code>, the maven coordinate of <a href="https://micronaut-projects.github.io/micronaut-problem-json/latest/guide/">Micronaut Problem JSON</a>, the same request:</p><pre><code class="language-bash">curl -i locahost:8080/api/v1/bogus</code></pre><p>returns:</p><pre><code class="language-json">{    &quot;type&quot;:&quot;about:blank&quot;,    &quot;status&quot;:404,    &quot;detail&quot;:&quot;Page Not Found&quot;}</code></pre><p>Moreover, the response content type is <code>application/problem+json</code>.</p><p><a href="https://micronaut-projects.github.io/micronaut-problem-json/latest/guide/">Micronaut Problem JSON</a> helps you produce application/problem+json responses from a Micronaut application. It connects the <a href="https://github.com/zalando/problem">Problem library</a> and <a href="https://docs.micronaut.io/latest/guide/#errorFormatting">Micronaut Error</a> Formatting capabilities.</p>]]></description><guid>micronaut-problem-json</guid><pubDate>Tue, 28 Sep 2021 11:07:35 GMT</pubDate></item><item><title>Micronaut Health Check</title><link>https://sergiodelamo.com/blog/exposing-a-health-endpoint.html</link><description><![CDATA[<p>The easiest way to expose a <a href="https://aws.amazon.com/builders-library/implementing-health-checks/">health check</a> in your Micronaut Application is to enable the <a href="https://docs.micronaut.io/latest/guide/#healthEndpoint">health endpoint</a>.</p><p>Just by adding the dependency <code>io.micronaut:micronaut-management</code> your application exposes a GET <code>/health</code> route which you can use as a health check.Add a <a href="https://sergiodelamo.com/blog/micronaut-test-health-endpoint.html">test which verifies the application exposes the health endpoint</a>.</p>]]></description><guid>exposing-a-health-endpoint</guid><pubDate>Tue, 28 Sep 2021 09:17:41 GMT</pubDate></item><item><title>Guest at S5E02 of the Groovy Podcast</title><link>https://sergiodelamo.com/blog/groovy-podcast-82.html</link><description><![CDATA[<p><a href="https://www.kousenit.com">Ken Kousen</a> invited me to talk at the <a href="https://groovypodcast.podbean.com/e/groovy-podcast-s05e02-82-with-sergio-del-amo/">Groovy Podcast</a>.</p><p>Subscribe to the <a href="https://feed.podbean.com/groovypodcast/feed.xml">Groovy Podcast Feed</a> in your podcast app of choice.</p><p><img src="https://images.sergiodelamo.com/groovypodcast_image.jpg" alt="" /></p>]]></description><guid>groovy-podcast-82</guid><pubDate>Thu, 23 Sep 2021 18:16:53 GMT</pubDate></item><item><title>How to render a GSP in a Grails Service</title><link>https://sergiodelamo.com/blog/how-to-render-a-gsp-in-a-grails-service.html</link><description><![CDATA[<p>You can render a model with a <a href="https://gsp.grails.org">GSP template</a> within a <a href="https://grails.org">Grails</a> service:</p><p>Given the following view:</p><p><code>grails-app/views/contact/_contact.gsp</code></p><pre><code class="language-groovy">&lt;h1&gt;${contact.name}&lt;/h1&gt;</code></pre><p>You can create a service such as:</p><pre><code class="language-groovy">import grails.gsp.PageRendererimport groovy.transform.CompileStatic@CompileStaticclass GspRenderingService {    PageRenderer groovyPageRenderer    String render(String templatePath, Map&lt;String, Object&gt; model) {        groovyPageRenderer.render(view: templatePath, model: model)    }}</code></pre><p>Which you can call from other Grails Artifacts:</p><pre><code class="language-groovy">gspRenderingService.render('/contact/_contact',[contact: new Contact(name: 'Sergio')])</code></pre><p><a href="https://twitter.com/mrhaki">@mrhaki</a> has a more comprehensive blog post (<a href="https://blog.mrhaki.com/2012/03/grails-goodness-render-gsp-views-and.html">Grails Goodness: Render GSP Views And Templates Outside Controllers</a>) about this feature.</p>]]></description><guid>how-to-render-a-gsp-in-a-grails-service</guid><pubDate>Wed, 22 Sep 2021 14:18:11 GMT</pubDate></item><item><title>How to use a JSON View in a Grails Service</title><link>https://sergiodelamo.com/blog/how-to-use-a-grails-json-view-in-a-service.html</link><description><![CDATA[<p>You can render a model into JSON with a <a href="http://views.grails.org/latest/#_json_views">JSON View</a> within a <a href="https://grails.org">Grails</a> service:</p><p>Given the following view:</p><p><code>grails-app/views/apiContact/_contact.gson</code></p><pre><code class="language-groovy">import example.Contactmodel {    Contact contact}json {    fullname contact.name}</code></pre><p>You can create a service such as:</p><pre><code class="language-groovy">package exampleimport grails.plugin.json.view.JsonViewTemplateEngineimport groovy.text.Templateimport org.springframework.beans.factory.annotation.Autowiredclass JsonViewRenderingService {    @Autowired    JsonViewTemplateEngine jsonViewTemplateEngine    Writable renderWritable(String templatePath, Map&lt;String, Object&gt; model) {        jsonViewTemplateEngine.resolveTemplate(templatePath)            .make(model)    }    String render(String templatePath, Map&lt;String, Object&gt; model) {        renderWritable(templatePath, model)            .writeTo(new StringWriter())            .toString()    }}</code></pre><p>Which you can call from other Grails Artifacts:</p><pre><code class="language-groovy">jsonViewRenderingService.render('/apiContact/_contact',[contact: new Contact(name: 'Sergio')])</code></pre><p>Kudos to <a href="https://twitter.com/virtualdogbert">@virtualdogbert</a> for this trick.</p>]]></description><guid>how-to-use-a-grails-json-view-in-a-service</guid><pubDate>Wed, 22 Sep 2021 13:56:34 GMT</pubDate></item><item><title>Test a Grails Application with Micronaut HTTP Client</title><link>https://sergiodelamo.com/blog/grails-micronaut-http-client.html</link><description><![CDATA[<p>To use a <a href="https://docs.micronaut.io/latest/guide/#httpClient">Micronaut HTTP Client</a> to test the API exposed by your Grails application you can use the following parent class for your integration tests:</p><pre><code class="language-groovy">package exampleimport io.micronaut.http.client.BlockingHttpClientimport io.micronaut.http.client.HttpClientimport spock.lang.Sharedimport spock.lang.Specificationimport spock.lang.Stepwiseabstract class ClientSpec extends Specification {    @Shared    HttpClient _httpClient    @Shared    BlockingHttpClient _client    HttpClient getHttpClient() {        if (_httpClient == null) {            _httpClient = createHttpClient()        }        _httpClient    }    BlockingHttpClient getClient() {        if (_client == null) {            _client = getHttpClient().toBlocking()        }        _client    }    HttpClient createHttpClient() {        String baseUrl = &quot;http://localhost:$serverPort&quot;        HttpClient.create(baseUrl.toURL())    }    def cleanupSpec() {        resetHttpClient()    }    void resetHttpClient() {        _httpClient?.close()        _httpClient = null        _client = null    }}</code></pre><p>A example test in <code>src/integrationTest/groovy</code>:</p><pre><code class="language-groovy">@Integrationclass ApiContactControllerSpec extends ClientSpec {    void &quot;/apiContact is not a valid path&quot;() {        when:        client.exchange(HttpRequest.GET('/apiContact'))        then:        HttpClientResponseException e = thrown()        e.status == HttpStatus.NOT_FOUND    }}</code></pre><p>You will need the following dependencies in <code>build.gradle</code>:</p><pre><code class="language-groovy">...dependencies {    ...    ..    testImplementation &quot;io.micronaut:micronaut-inject-groovy&quot;    testImplementation &quot;io.micronaut:micronaut-http-client&quot;}</code></pre>]]></description><guid>grails-micronaut-http-client</guid><pubDate>Wed, 22 Sep 2021 13:45:53 GMT</pubDate></item><item><title>Localized error message for a Grails Validateable POGO's field</title><link>https://sergiodelamo.com/blog/grails-validateable-message-source.html</link><description><![CDATA[<p>I often decorate my <a href="https://grails.org">Grails</a> Controllers with <a href="https://docs.groovy-lang.org/next/html/documentation/core-traits.html">Traits</a>. I often use is the following trait:</p><pre><code class="language-groovy">@CompileStatictrait ValidateableMessageSource {    MessageSource messageSource    Optional&lt;String&gt; errorMessage(Validateable validateable,                                  String fieldName) throws NoSuchMessageException {        FieldError fieldError = validateable.errors?.getFieldError(fieldName)        if (!fieldError) {            return Optional.empty()        }        Optional.of(messageSource.getMessage(fieldError, LocaleContextHolder.locale))    }}</code></pre><p>I use a POGO to bind the HTTP Request body.</p><pre><code class="language-groovy">import grails.compiler.GrailsCompileStaticimport grails.validation.Validateable@GrailsCompileStaticclass ContactSave implements Validateable {    String name    static constraints = {        name blank: false    }}</code></pre><p>This is how I use the trait in a controller:</p><pre><code class="language-groovy">class ContactController implements ValidateableMessageSource {    ...        def save(ContactSave contactSave) {        if (contactSave.hasErrors()) {            String invalid = errorMessage(contactSave, &quot;name&quot;).orElse(null)            ContactCreate contactCreate = new ContactCreate(name: contactSave.name,                                                        nameInvalid: invalid)            render view: 'create', model: [contact: contactCreate]            return        }        String id = contactService.save(contactSave)        redirect(action: 'show', id: id)    }}</code></pre>]]></description><guid>grails-validateable-message-source</guid><pubDate>Wed, 22 Sep 2021 13:10:02 GMT</pubDate></item><item><title>Extra Grails Naming Conventions</title><link>https://sergiodelamo.com/blog/grails-extra-conventions.html</link><description><![CDATA[<p>The <a href="https://grails.org">Grails</a> framework is a covention over configuration framework. Through the years, I have created extra naming conventions.</p><h2>Exisiting naming conventions</h2><p>There are several conventions around naming already:</p><ul><li><code>Controller</code> is the suffix for Controllers. For example: <code>BookController.groovy</code>. They are in the folder <code>grails-app/controllers</code>.</li><li><code>Service</code> is the suffix for services. For example: <code>BookInventoryService</code>. They are in the folder <code>grails-app/services</code>.</li><li><code>TagLib</code> is the suffix for tag libraries. For example: <code>BootstrapTagLib</code>. They are in the folder <code>grails-app/taglib</code></li><li><code>UrlMappings</code> is the suffix for the <a href="http://docs.grails.org/latest/guide/theWebLayer.html#urlmappings">URL mappings</a>. You can have multiple files ending with <code>UrlMappings</code> and they are in the folder <code>grails-app/controllers</code>.</li><li><code>Spec</code> for Spock specifications. They are in the folders <code>src/test/groovy</code> or <code>src/integrationTest/groovy</code></li></ul><h2>Extra naming conventions suffixes</h2><p>I use these suffixes:</p><ul><li><code>Entity</code></li><li><code>GormService</code></li><li><code>Listener</code></li><li><code>ListenerService</code></li><li><code>JobService</code></li><li><code>Utils</code></li><li><code>Client</code></li><li><code>GebSpec</code></li><li><code>Page</code></li><li><code>Module</code></li><li><code>Create</code></li><li><code>Save</code></li><li><code>Update</code></li></ul><h3><code>Entity</code> suffix for Domain classes</h3><ul><li>Domain classes are in the folder <code>grails-app/domain</code>. I like to suffix them with <code>Entity</code> and remove the <code>Entity</code> part from the table name using the <a href="https://docs.grails.org/latest/ref/Domain%20Classes/mapping.html"><code>mapping</code> static property</a></li></ul><pre><code class="language-groovy">class BookEntity {    String title    String about    static mapping = {        about type: 'text'        table 'book'    }}</code></pre><h3><code>GormService</code> suffix for GORM related services</h3><p>I encapsulate <a href="https://gorm.grails.org">GORM</a> logic in a few services in the <code>grails-app/services</code> folder. I like to suffix them with <code>GormService</code>. I have used <code>DataService</code> in the past but lately I have settled with <code>GormService</code>. E.g.</p><pre><code class="language-groovy">@Service(BookEntity)interface BookService {    BookEntity save(String title, String about)}</code></pre><h3>Suffix <code>Listener</code> for Kakfa, RabbmitMQ listeners</h3><p>If I integrate a Grails application with <a href="https://micronaut-projects.github.io/micronaut-kafka/latest/guide/">Micronaut Kafka</a> or <a href="https://micronaut-projects.github.io/micronaut-rabbitmq/latest/guide/">Micronaut RabbitMQ</a> I use the <code>Listener</code> suffix for those classes.</p><pre><code class="language-groovy">@KafkaListenerclass AnalyticsListener {    @Inject    BookAnalyticsGormService bookAnalyticsGormService    @Topic('analytics')    void updateAnalytics(Map payload) {        ...    }}</code></pre><p>These listeners are in the folder <code>src/main/groovy</code></p><h3>Suffix <code>Listener</code> for classes subscribing to events</h3><p>I suffix with <code>ListenerService</code> services containing methods annotated with <code>@Subscriber</code>.</p><pre><code class="language-groovy">class BookSavedListenerService {    @Transactional    @Subscriber     void saveBook(Book book) {    ...</code></pre><p>Also, classes extending <code>AbstractPersistenceEventListener</code> such as the <code>UserPasswordEncoderListener</code> which you get when you execute <code>s2-quickstart</code> with <a href="https://grails-plugins.github.io/grails-spring-security-core/">Grails Spring Security Core Plugin</a></p><p>I use the suffix <code>ListenerService</code> for classes in <code>grails-app/services</code>.</p><p>I use the suffix <code>Listener</code> for classes in <code>src/main/groovy</code>.</p><h3>Suffix <code>JobService</code> for Jobs</h3><p>I use the suffix <code>JobService</code> for classes in <code>grails-app/services</code> with methods annotated with <code>org.springframework.scheduling.annotation.Scheduled</code>:</p><pre><code class="language-groovy">@Slf4j@CompileStaticclass DailyEmailJobService  {    static lazyInit = false     EmailService emailService     @Scheduled(cron = &quot;0 30 4 1/1 * ?&quot;)     void execute() {    ...}</code></pre><h3>Suffix <code>Utils</code> for utility classes.</h3><p>I use the suffix <code>Utils</code> for classes in <code>grails-app/utils</code>. For example <code>EAN13Utils.groovy</code>. <code>Utils</code> classe are mostly final classes with static methods.</p><h3>Sufix <code>Client</code> for HTTP Clients.</h3><p>I use the suffix <code>Client</code> for declarative Micronaut HTTP Client in <code>src/main/groovy</code>.</p><pre><code class="language-groovy">package example.grailsimport io.micronaut.http.annotation.Getimport io.micronaut.http.client.annotation.Client@Client(id = &quot;itunes&quot;) interface ItunesClient {    @Get(&quot;/search?limit=25&amp;media=music&amp;entity=album&amp;term={term}&quot;)     SearchResult search(String term)</code></pre><h3>Suffix <code>GebSpec</code> for Geb integration tests</h3><p>I use the suffix <code>GebSpec</code> for Grails Integration tests which use <a href="https://gebish.org">Geb</a>. They are in the folder <code>src/integrationTest/groovy</code>. For example:</p><pre><code class="language-groovy">@Integrationclass ContactPageGebSpec extends GebSpec {    void &quot;go to contacts index page&quot;() {        when:        to(ContactPage)        then:        at(ContactPage)}</code></pre><h3>Suffix <code>Page</code> for Geb Pages</h3><p>I use the suffix <code>Page</code> for <a href="https://gebish.org/manual/current/#scripting-with-page-objects">Geb Page Objects</a>. They are in the folder <code>src/integrationTest/groovy</code>. For example:</p><pre><code class="language-groovy">class ContactPage extends Page {    static url = '/'    static at = {        $('div#container-contact')    }    static content = {        navbar { $('#contact-navbar').module(AddContactModule) }    }    void addContact() {        navbar.add()    }}</code></pre><h3>Suffix <code>Module</code> for Geb Modules</h3><p>I use the suffix <code>Module</code> for <a href="https://gebish.org/manual/current/#modules">Geb Modules</a>. They are in the folder <code>src/integrationTest/groovy</code>. For example:</p><pre><code class="language-groovy">import geb.Moduleclass AddContactModule extends Module {    static content = {        addContactLink(to: AddContactPage) { $('a', text: contains('Add Contact')) }    }    void add() {        addContactLink.click()    }}</code></pre><h3>Suffix <code>Create</code>, <code>Update</code> and <code>Save</code> for POGOs used in the create, update and save actions</h3><p>I like to use a different POGO for the create action and the save action.</p><p>The <code>Create</code> POGO normally allows anything to be null and it is the object that the controller passes to the view (e.g. the GSP) as the model.</p><pre><code class="language-groovy">import grails.compiler.GrailsCompileStaticimport grails.validation.Validateable@GrailsCompileStaticclass ContactCreate implements Validateable {    String name    String nameInvalid    static constraints = {        name nullable: true        nameInvalid nullable: true    }}</code></pre><p>The <code>Save</code> POGO contains the constraints which are validated before passing the object into the service layer:</p><pre><code class="language-groovy">import grails.compiler.GrailsCompileStaticimport grails.validation.Validateable@GrailsCompileStaticclass ContactSave implements Validateable {    String name    static constraints = {        name blank: false    }}</code></pre><p>This is an example of how I use them in a controller:</p><pre><code class="language-groovy">class ContactController {    ...    def create() {        [contact: new ContactCreate()]    }    def save(ContactSave contactSave) {        if (contactSave.hasErrors()) {        String invalid = errorMessage(contactSave, &quot;name&quot;).orElse(null)        ContactCreate contactCreate = new ContactCreate(name: contactSave.name,                                                        nameInvalid: invalid)            render view: 'create', model: [contact: contactCreate]            return        }        String id = contactService.save(contactSave)        redirect(action: 'show', id: id)    }}</code></pre><h2>Don't make me think</h2><p>I became aware of the Book <a href="https://www.amazon.com/Dont-Make-Me-Think-Usability/dp/0321344758">Don't make me think</a> while attending a attending a usability class at the unversity. It had a big impact on me when I read it. I always felt the same about Grails naming conventions, they don't make you think and they keep me productive.</p>]]></description><guid>grails-extra-conventions</guid><pubDate>Wed, 22 Sep 2021 11:01:10 GMT</pubDate></item><item><title>Add a Show by URL with Apple Podcasts</title><link>https://sergiodelamo.com/blog/add-a-show-by-url-with-apple-podcasts.html</link><description><![CDATA[<p>Useful for podcasts not listed in public Podcast directories - subscription podcasts, company podcasts.</p><p><a href="https://www.apple.com/apple-podcasts/">Apple Podcasts</a>:</p><p><img src="https://images.sergiodelamo.com/add-a-show-by-url.png" alt="" /></p>]]></description><guid>add-a-show-by-url-with-apple-podcasts</guid><pubDate>Tue, 21 Sep 2021 05:44:18 GMT</pubDate></item><item><title>&#x1f5e3; Micronaut Webinar at Bucharest JUG</title><link>https://sergiodelamo.com/blog/micronaut-webinar-bucharest-2021-09-16.html</link><description><![CDATA[<p>Next thursday, I do an online Micronaut talk at Bucharest Java Microservices User Group</p><p>📅 Dates: September 16⏳ Time: 6:00 p.m. to 08:00 p.m. EEST📍 Location: Online.<br />👨🏻‍🏫 Instructor: Yours Truly.<br />💵 Registration Fee: Free</p><p><a href="https://www.meetup.com/Java-Microservices-UsersGroup-Bucharest/events/279652591/">Go to the linked site</a></p>]]></description><guid>micronaut-webinar-bucharest-2021-09-16</guid><pubDate>Tue, 14 Sep 2021 11:30:26 GMT</pubDate></item><item><title>Useful docker commands</title><link>https://sergiodelamo.com/blog/useful-docker-commands.html</link><description><![CDATA[<p><strong>stop all containers:</strong></p><pre><code class="language-bash">docker kill $(docker ps -q)</code></pre><p><strong>remove all containers</strong></p><pre><code class="language-bash">docker rm $(docker ps -a -q)</code></pre><p><strong>remove all docker images</strong></p><pre><code class="language-bash">docker rmi $(docker images -q)</code></pre>]]></description><guid>useful-docker-commands</guid><pubDate>Sun, 12 Sep 2021 05:56:55 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 017 - Último Cuatrimestre 2021</title><link>https://sergiodelamo.com/blog/codigobot017.html</link><description><![CDATA[<p>Hablo con <a href="https://kinisoftware.com">Kini</a> sobre los objetivos para el último cuatrimestre del 2021 y los proyectos a la vuelta de la esquina.</p><p><a href="https://codigobot.com/017.html"><img src="https://media.codigobot.com/artwork-landscape.png" alt="" /></a></p><p><a href="https://codigobot.com/017.html">Go to the linked site</a></p>]]></description><guid>codigobot017</guid><pubDate>Sat, 11 Sep 2021 07:25:00 GMT</pubDate></item><item><title>Micronaut Pushover released</title><link>https://sergiodelamo.com/blog/released-micronaut-pushover-101.html</link><description><![CDATA[<p>I've released a <a href="https://micronaut.io">Micronaut</a> Java library to consume the <a href="https://pushover.net/api">Pushover API</a>. It is built with the <a href="https://micronaut.io">Micronaut® framework</a> and you can use it in a Micronaut app or as a standalone library.</p><h2>Installation</h2><p>To use it with <a href="https://gradle.org">Gradle</a>:</p><pre><code class="language-groovy">dependencies {    ...    ..    implementation 'com.softamo:pushover:1.0.1'}</code></pre><p>To use it with <a href="https://maven.apache.org">Maven</a>:</p><pre><code class="language-xml">&lt;dependency&gt;	&lt;groupId&gt;com.softamo&lt;/groupId&gt;	&lt;artifactId&gt;pushover&lt;/artifactId&gt;	&lt;version&gt;1.0.1&lt;/version&gt;	&lt;type&gt;pom&lt;/type&gt;&lt;/dependency&gt;</code></pre><h2>Usage</h2><h3>Micronaut Applications</h3><p>If you want to use the library in Micronaut application, register applications and users via configuration:</p><pre><code class="language-yaml">pushover:	applications:		l3-37:			token: 'T6xoNWc7zboEppeeM69tMZsCNkdRqU'	users:		sdelamo:			key: 's2HkfXVenEeMJ2MBwqDZrhAXpg7uzK',</code></pre><p>Then you can send messages from an application to a user:</p><pre><code class="language-java">@Controllerpublic class HomeController {	private static final Logger LOG = LoggerFactory.getLogger(HomeController.class);	private final PushoverApplication application;	private final PushoverUser user;	private final Scheduler scheduler;	HomeController(@Named(TaskExecutors.IO) ExecutorService executorService,				   @Named(&quot;l3-37&quot;) PushoverApplication application,				   @Named(&quot;sdelamo&quot;) PushoverUser user) {		this.scheduler = Schedulers.fromExecutorService(executorService);		this.application = application;		this.user = user;	}		@Get	@Produces(MediaType.TEXT_PLAIN)	String index() {		Message message = Message.builder(&quot;Hello World&quot;).sound(Sound.ALIEN).build();		Mono.from(application.send(user, message))				.subscribeOn(scheduler)				.subscribe(response -&gt; {					if (response.getStatus().equals(1)) {						LOG.trace(&quot;your notification has been received and queued. Request {}&quot;, response.getRequest());					} else {						LOG.trace(&quot;your notification could not be delivered. status {}&quot;, response.getStatus());					}				});		return &quot;Hello world&quot;;	}}</code></pre><p>Moreover, For Micronaut Applications you can annotate your methods with <code>@PushoverMessage</code></p><pre><code class="language-java">......@Controllerpublic class HomeController {	@PushoverMessage(value = &quot;User greeted&quot;,			url = &quot;https://sergiodelamo.com&quot;,			sound = Sound.ALIEN,			appName = &quot;l3-37&quot;			userName = &quot;sdelamo&quot;,			urlTitle = &quot;blog&quot;)	@Get	@Produces(MediaType.TEXT_PLAIN)	String index() {		return &quot;Hello world&quot;;	}}</code></pre><p>If you have only one application and user configured, you can skip <code>appName</code> and <code>userName</code> members.</p><pre><code class="language-java">......@Controllerpublic class HomeController {	@PushoverMessage(value = &quot;User greeted&quot;,			url = &quot;https://sergiodelamo.com&quot;,			sound = Sound.ALIEN,			urlTitle = &quot;blog&quot;)	@Get	@Produces(MediaType.TEXT_PLAIN)	String index() {		return &quot;Hello world&quot;;	}}</code></pre><h3>Without Micronaut® framework</h3><p>You can use the library without a Micronaut Application Context. In that case, you can do:</p><pre><code class="language-java">PushoverUser user = new PushoverUser() {	@Override	public String getKey() {		return &quot;s2HkfXVenEeMJ2MBwqDZrhAXpg7uzK&quot;;	}	@Override	public String getName() {		return &quot;sdelamo&quot;;	}};PushoverApplication application = new PushoverApplication(new PushoverApplicationConfiguration() {	@Override	public String getToken() {		return &quot;T6xoNWc7zboEppeeM69tMZsCNkdRqU&quot;;	}	@Override	public String getName() {		return &quot;l3-37&quot;;	}},new ManualPushoverHttpClient()); Message message = Message.builder(&quot;Hello World&quot;).sound(Sound.ALIEN).build();Response result = Mono.from(application.send(user, message)).block()</code></pre><ul><li><a href="https://github.com/sdelamo/pushover">Code Repository</a></li><li><a href="https://github.com/sdelamo/pushover/releases">Releases</a></li></ul><p>I hope you find it as useful as I do.</p>]]></description><guid>released-micronaut-pushover-101</guid><pubDate>Wed, 08 Sep 2021 04:58:18 GMT</pubDate></item><item><title>SAFRI.NET Micronaut Foundation™ sponsor</title><link>https://sergiodelamo.com/blog/safri-micronaut-corporate-sponsor.html</link><description><![CDATA[<p><a href="https://micronaut.io">Micronaut® framework</a> is an Open Source JVM Framework licensed via <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 License</a>.</p><p>Often, we use Open Source products and we take them for granted. Moreover, our organisations use those open-source solutions to developer our products. The health of any Open Source solution, which we rely on, affects the health of our technology stack. Hence, it affects the health our products. How easy is to move them forward, how easy is to hire developers interested in the technologies we use. There are many way an organization can support an Open Source product:</p><ul><li>Sponsoring events which feature that technology.</li><li>Allowing their employees to learn, advocate and do public speaking for those frameworks in their company time.</li></ul><p>However, another way is to support them directly with money.Some programmers develop Open Source products on their free time. However, most big open source projects often rely on programmers whose companies, often involved in the stewardship of the Open Source technology, allocate them full-time on working on those products. Without people working full-time on them it is hard to keep them going with the necessary pace.</p><p>Because of that, I was happy when we <a href="https://micronaut.io/2020/06/29/micronaut-foundation-announced/">announced the Micronaut Foundation</a> back in 2020. The <a href="https://micronaut.io/foundation/">Micronaut Foundation</a> created not just a governance body to ensure the framework advances and continues to serve a global community. It created the means to collect money to fuel the development of the framework.</p><p>One of those vehicles is the <a href="https://micronaut.io/foundation/corporate-sponsorship/">Micronaut Foundation Corporate Sponsorship</a>.</p><p><a href="https://www.safri.net">SAFRI.NET</a>, a german professional IT services provider, joined <a href="https://micronaut.io/2021/09/02/new-bronze-level-corporate-sponsor-safri-net/">Micronaut Foundation as a Bronze-Level Corporate Sponsor</a>.</p><blockquote><p>Micronaut is an important part in our projects and contributes immensely to our productivity and success. For this reason, and because we inherently like to give back to the open source communities, we decided to do the same for Micronaut.</p></blockquote><p>Many thanks to <a href="https://www.safri.net">SAFRI.NET</a> for their support of the Micronaut® framework and everyone, myself included, involved with it.</p><p><a href="https://micronaut.io/2021/09/02/new-bronze-level-corporate-sponsor-safri-net/">Go to the linked site</a></p>]]></description><guid>safri-micronaut-corporate-sponsor</guid><pubDate>Mon, 06 Sep 2021 12:43:44 GMT</pubDate></item><item><title>Pushover - Simple notifications</title><link>https://sergiodelamo.com/blog/pushover.html</link><description><![CDATA[<p>I discovered <a href="https://pushover.net">Pushover</a> via Sixcolors's post - <a href="https://sixcolors.com/post/2021/09/tools-we-used-to-make-the-upgrade-call-in-podcast/">Tools we used to make a call-in podcast</a>.</p><blockquote><p>Pushover makes it easy to get real-time notifications on your Android, iPhone, iPad, and Desktop (Android Wear and Apple Watch, too!)</p></blockquote><p>It is simple to integrate:</p><blockquote><p>For developers, system administrators, and everyone with just some technical savvy, our API makes it easy to integrate Pushover into your web app, network monitor, shell script, and anything else you can think of to send notifications to yourself or thousands of users.</p></blockquote><p>It is such a useful idea. I tried it and it works like a charm.</p><p><img src="https://images.sergiodelamo.com/pushovernotification.jpg" alt="" /></p><p>Moreover, I have started an Open Source <a href="https://github.com/sdelamo/pushover">Micronaut Java library for Pushover</a>. I hope to release a version of it soon.</p>]]></description><guid>pushover</guid><pubDate>Sun, 05 Sep 2021 10:46:59 GMT</pubDate></item><item><title>The Story of Playdate Podcast</title><link>https://sergiodelamo.com/blog/play-date-podcast.html</link><description><![CDATA[<p>I listened to Panic's Podcast latest episode - <a href="https://podcast.panic.com/#episode006">The Story of Playdate</a>. I gave up and I purchased a <a href="https://play.date">Playdate</a>.</p><p>When I was a child I had consoles (Atari, MasterSystem, Gameboy) and I played a lof of PC games but I don't play anymore. I have no console. I do not play games in my phone either. However, I love <a href="https://panic.com">Panic</a>'s software. I am writing this post with <a href="https://nova.app">Nova</a>. I use <a href="https://panic.com/transmit/">Transmit</a> daily. I use <a href="https://panic.com/prompt/">Prompt</a> in my iOS devices and I had use other Panic's products such as <a href="https://panic.com/coda/">Coda</a> and <a href="https://panic.com/blog/the-panic-status-board/">Status Board</a>.</p><p>I purchased Playdate because I am intrigued with the device, the whole development experience SDK, to support Panic and to bring me back to my Gameboy days. Moreover, I have two small kids. It will be a nice suprise for them as well.</p><p><a href="https://podcast.panic.com/#episode006">Go to the linked site</a></p>]]></description><guid>play-date-podcast</guid><pubDate>Sat, 04 Sep 2021 08:43:16 GMT</pubDate></item><item><title>Colorblind Accessibility Manifesto</title><link>https://sergiodelamo.com/blog/colorblind-accessibility-manifesto.html</link><description><![CDATA[<p>A reminder about how important and widespread color  blindness is.</p><blockquote><p>Changing the button color on your website may seem insignificant, but it could make that website inaccessible to nearly 8% of men and 0.4% of women who have color blindness.</p></blockquote><p><a href="https://www.wired.com/news/columns/cultofmac/0,71956-0.html">Steve Jobs</a> once said:</p><blockquote><p>Most people make the mistake of thinking design is what it looks like. That’s not what we think design is. It’s not just what it looks like and feels like. Design is how it works.</p></blockquote><p>The best way to make something work is to make it accesible to everyone.</p><p><a href="https://colorblindaccessibilitymanifesto.com">Go to the linked site</a></p>]]></description><guid>colorblind-accessibility-manifesto</guid><pubDate>Fri, 03 Sep 2021 17:46:04 GMT</pubDate></item><item><title>Grails Deep Dive Course</title><link>https://sergiodelamo.com/blog/grails-deep-dive-2021.html</link><description><![CDATA[<p>My colleague <a href="https://twitter.com/ZacharyAKlein">@ZacharyAKlein</a> teaches a <a href="https://grails.org">Grails</a> course next week. Zach has a lot of experience with Grails and he his a wonderful instructor. It is not too late to learn about Grails - arguably the most productive JVM framework.</p><p>🗓 Dates: Sept. 7 to Sept. 16<br />⏰ Time: 9:00 a.m. to 12:00 p.m. CDT<br />📍 Location: Online<br />🧑‍🏫 Instructor: Zachary Klein<br />💵 Registration Fee: $199.00 USD</p><p><a href="https://objectcomputing.com/services/training/catalog/grails/grails-deep-dive">Go to the linked site</a></p>]]></description><guid>grails-deep-dive-2021</guid><pubDate>Thu, 02 Sep 2021 16:18:02 GMT</pubDate></item><item><title>HTML Keyboard Shortcuts with accesskey</title><link>https://sergiodelamo.com/blog/adding-shortcuts-to-your-httml-page-with-accesskey.html</link><description><![CDATA[<p>I added keyboard shortcuts to this website with <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey">accesskey</a> HTML attribute.</p><blockquote><p>The accesskey global attribute provides a hint for generating a keyboard shortcut for the current element. The attribute value must consist of a single printable character (which includes accented and other characters that can be generated by the keyboard).</p></blockquote><blockquote><p>The way to activate the accesskey depends on the browser and its platform.</p></blockquote><p>On MacOS:</p><ul><li><code>OPTION + CONTROL + b</code> opens the <a href="https://sergiodelamo.com/blog/index.html">Blog</a> page.</li><li><code>OPTION + CONTROL + p</code> opens the <a href="https://sergiodelamo.com/index.html">Projects</a> page.</li><li><code>OPTION + CONTROL + c</code> opens the <a href="https://sergiodelamo.com/contact.html">Contact</a> page.</li><li><code>OPTION + CONTROL + t</code> opens the <a href="https://sergiodelamo.com/blog/tag/index.html">Tags</a> page.</li><li><code>OPTION + CONTROL + s</code> opens the <a href="https://sergiodelamo.com/blog/tag/talk.html">Talks</a> page.</li><li><code>OPTION + CONTROL + m</code> opens the <a href="https://sergiodelamo.com/me.html">Me</a> page.</li><li><code>OPTION + CONTROL + c</code> opens the <a href="https://sergiodelamo.com/cv.html">CV</a> page.</li></ul><p>In the blog page, <code>OPTION + CONTROL + 1</code> opens the latest post.</p>]]></description><guid>adding-shortcuts-to-your-httml-page-with-accesskey</guid><pubDate>Wed, 01 Sep 2021 06:41:51 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 016 - Alexa Live 2021</title><link>https://sergiodelamo.com/blog/codigobot016.html</link><description><![CDATA[<p>Hablo con Kini del Alexa Live 2021.</p><p><a href="https://codigobot.com/016.html"><img src="https://media.codigobot.com/artwork-landscape.png" alt="" /></a></p><p><a href="https://codigobot.com/016.html">Go to the linked site</a></p>]]></description><guid>codigobot016</guid><pubDate>Fri, 27 Aug 2021 06:54:39 GMT</pubDate></item><item><title>Micronaut CLI Github Activity</title><link>https://sergiodelamo.com/blog/micronaut-cli-githubactivity.html</link><description><![CDATA[<p>At <a href="https://objectcomputing.com/">OCI</a>, we report what we have been working on each day. Thus, I have to keep track of everything I have been doing. However, in a typical day jump from project to project, from issue to issue, in the <a href="https://github.com/micronaut-projects">micronaut-projects</a> Github organization. It is challenging to keep track of everything that I have been doing.</p><p>To help me, I have written a Micronaut CLI application which queries the <a href="https://docs.github.com/en/rest/reference/activity#events">Github events API</a>. It is available in the repository <a href="https://github.com/sdelamo/githubactivity">sdelamo/githubactivity</a>.</p><p>You can build the CLI and get usage information:</p><pre><code class="language-bash">$ ./gradlew build$ java -jar build/libs/githubactivity-0.1-all.jar Missing required option: '--token'Usage: ghactivity [-hV] -t [-d=&lt;days&gt;] [-o=&lt;organization&gt;] [--type=&lt;type&gt;]                 [-u=&lt;user&gt;]... -d, --days=&lt;days&gt;   number of days for which to fetch events -h, --help          Show this help message and exit. -o, --org, --organization=&lt;organization&gt;                     Github organization -t, --token         Github personal Token     --type=&lt;type&gt;   Github's repository type -u, --user=&lt;user&gt;   Github's username -V, --version       Print version information and exit.</code></pre><p>To get a list of what you did in the past 7 days:</p><pre><code class="language-bash">% java -jar build/libs/githubactivity-0.1-all.jar -d 7 -tEnter value for --token (Github personal Token): XXXX........# 2021-08-13## micronaut-security754 doc: since 2.5. security rule cannot access Body749 doc: add back notes about deprecated methods removal725 test: remove pending feature## micronaut-guides431 Update rotation publication Guide## micronaut-build182 fix: broken ConfigurationProperties Nested links180 Links to ConfigurationProperties table header for Nested classes are broken 179 Bump asciidoctorj from 1.5.6 to 2.5.2178 Bump ncipollo/release-action from 1.8.6 to 1.8.8176 Bump spotless-plugin-gradle from 5.12.5 to 5.14.2175 Bump jsoup from 1.7.3 to 1.14.1170 Bump jruby-complete from 1.7.26 to 9.2.19.0167 Bump snakeyaml from 1.28 to 1.29165 Bump gradle/wrapper-validation-action from 1.0.3 to 1.0.4164 Bump actions/cache from 2.1.5 to 2.1.6</code></pre><p>The CLI queries every repo in the organization and collects your activity.</p><p>If you work a lot with Github projects, this CLI application may come in handy.</p>]]></description><guid>micronaut-cli-githubactivity</guid><pubDate>Fri, 20 Aug 2021 17:21:28 GMT</pubDate></item><item><title>Version 2.0.0 of Eurorates Micronaut Library released</title><link>https://sergiodelamo.com/blog/eurorates-micronaut-library-2.0.0.html</link><description><![CDATA[<h2>Reactive Streams implementation agnostic</h2><p><a href="https://micronaut.io/2021/08/18/micronaut-framework-3-released/">Micronaut® framework 3</a> no longer exposes a Reactive Streams implementation :</p><blockquote><p>Previous releases of the Micronaut® framework included RxJava2 as a transitive dependency, and RxJava2 was the reactive streams implementation used to implement many features within the Framework. The Micronaut® framework now no longer exposes any reactive streams implementation by default. In addition, all usages of RxJava2 internally have been replaced with Project Reactor.</p></blockquote><p>It makes sense. Micronaut® framework is build agnostic. You can use Gradle or Maven. Micronaut® framework is JVM programming language agnostic. You can code in Java, Groovy or Kotlin. Now, it is reactive streams agnostic. You choose your favourite poison.</p><h2>Eurorates updated to Micronaut® framework 3</h2><p>I have released version 2.0.0 of <a href="https://github.com/sdelamo/eurorates">Eurorates</a>, a tiny <a href="https://micronaut.io">Micronaut</a> Java library which helps you consume the <a href="https://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html">Euro foreign exchange reference rates</a>. This new mayor release updates to Micronaut® framework 3 and changes the API to be Reactive Streams implementation agnostic. The library does not expose Rx Java 2 or any other reactive streams implementation library as transitive dependency anymore.</p><p>The main interface used to be:</p><pre><code class="language-java">public interface EuroRatesApi {     Single&lt;GesmesEnvelope&gt; currentReferenceRates();     Single&lt;GesmesEnvelope&gt; historicalReferenceRates();     Single&lt;GesmesEnvelope&gt; last90DaysReferenceRates(); }</code></pre><p>Now, it is:</p><pre><code class="language-java">public interface EuroRatesApi {     @SingleResult     Publisher&lt;GesmesEnvelope&gt; currentReferenceRates();     @SingleResult     Publisher&lt;GesmesEnvelope&gt; historicalReferenceRates();     @SingleResult     Publisher&lt;GesmesEnvelope&gt; last90DaysReferenceRates(); }</code></pre><p>The API exposes a <code>org.reactivestreams.Publisher</code>. Moreover, I annotate the methods with Micronaut annotation <a href="https://docs.micronaut.io/latest/api/io/micronaut/core/async/annotation/SingleResult.html"><code>@SingleResult</code></a>.</p><blockquote><p>Annotation that can be used to describe that an API emits a single result even if the return type is a Publisher.</p></blockquote><p>We encourage library authors to write reactive streams agnostic libraries.</p><p>I use this library myself, for example, in Telegram bot: <a href="https://exchangeratesbot.com">@ForeignExchangeRatesBot</a>.</p>]]></description><guid>eurorates-micronaut-library-2.0.0</guid><pubDate>Fri, 20 Aug 2021 16:06:51 GMT</pubDate></item><item><title>Gradle Podcast Plugin 0.0.5</title><link>https://sergiodelamo.com/blog/new-version-of-podcast-gradle-plugin.html</link><description><![CDATA[<p>I have released a new version of the <a href="https://plugins.gradle.org/plugin/groovycalamari.podcast">Gradle Podcast Plugin</a>. The plugin uses internally <a href="https://micronaut-projects.github.io/micronaut-rss/latest/guide/#itunespodcast">Micronaut RSS</a> module. I updated it to Micronaut 3.</p><p>I use the plugin to generate the  RSS feed of <a href="https://codigobot.com">Código Bot</a>.  Check it out.</p><p><a href="https://plugins.gradle.org/plugin/groovycalamari.podcast">Go to the linked site</a></p>]]></description><guid>new-version-of-podcast-gradle-plugin</guid><pubDate>Fri, 20 Aug 2021 14:49:59 GMT</pubDate></item><item><title>Submit your podcast to Amazon Music</title><link>https://sergiodelamo.com/blog/apple-music-podcast.html</link><description><![CDATA[<p>I submitted <a href="https://codigobot.com">Código Bot</a> to <a href="https://www.amazon.com/music/lp/podcasts">Amazon Music Podcasts</a>.</p><p><a href="https://podcasters.amazon.com/">Add or claim or podcast</a>.</p><p>You provide the feed:</p><p><img src="https://images.sergiodelamo.com/apple-music-podcasts-add.png" alt="" /></p><p>They send an email to the podcast author's email.</p><p><img src="https://images.sergiodelamo.com/apple-music-podcasts-email-sent.png" alt="" /></p><p><img src="https://images.sergiodelamo.com/apple-music-podcasts-you-are-all-set.png" alt="" /></p><p>Less than one hour later, the podcast got listed.</p><p>Listen to <a href="https://music.amazon.es/podcasts/8c000130-e0aa-4b66-8548-223f4c8d0f88/código-bot">Código Bot in Amazon Music</a>.</p>]]></description><guid>apple-music-podcast</guid><pubDate>Thu, 19 Aug 2021 09:31:24 GMT</pubDate></item><item><title>Micronaut Guides updated to 3!</title><link>https://sergiodelamo.com/blog/micronaut-guides-micronaut-3.html</link><description><![CDATA[<p><a href="https://micronaut.io/guides/">Micronaut Guides</a> are step by step tutorials to help you learn the <a href="https://micronaut.io">Micronaut® framework</a>.</p><p>My colleague <a href="https://twitter.com/ilopmar">Iván López</a> wrote about the <a href="https://micronaut.io/2021/04/12/improving-the-micronaut-guides-infrastructure/">improvements to Micronaut Guides infrastructure</a> back in April. We have continued to improve and automate the process.</p><p>Thanks to this focus towards automation, <a href="https://guides.micronaut.io">Micronaut Guides</a> are updated to the latest version of the framework. <a href="https://micronaut.io/2021/08/18/micronaut-framework-3-released/">Micronaut® framework 3.0.0 was released today</a>. There are 53 guides. Many of them with up to 6 combinations (3 languages and 2 build tools). Every guide contains a ZIP file with the sample of code written with Micronaut® framework 3.0.0. Every guide contains code samples. Every code sample uses 3.0.0.</p><h2>Guides versions</h2><p>We version Micronaut Guides. You can find the latest (using Micronaut® framework 3.0.0) at:</p><p><a href="https://micronaut.io/guides/">https://micronaut.io/guides/</a></p><p>But if you want to check guides for 2.5.x, you can find them at:</p><p><a href="https://guides.micronaut.io/2.5.x/index.html">https://guides.micronaut.io/2.5.x/index.html</a></p><h2>From 2.5.x to 3.0.x</h2><p>The <a href="https://github.com/micronaut-projects/micronaut-guides/pull/388">pull request to update Micronaut Guides to 3.0.0</a> is a good representation of the changes necessary to update projects from Micronaut® framework 2.5.x to 3.0.x.</p><ul><li>Replacements of <code>import javax.inject</code> with <code>import jakarta.inject</code>.</li><li>Migration from <a href="https://github.com/ReactiveX/RxJava/tree/2.x">RxJava2</a> to <a href="https://projectreactor.io">Project Reactor</a>.</li><li>Changes in the security guides. Micronaut Security 3 removes <code>UserDetails</code> in favour of <code>Authentication</code>. <code>Authentication</code> and <code>AuthenticationResponse</code> have new static methods to build instances.</li><li>Small changes (package renaming, use of <code>BeanProvider</code>, ...)</li></ul><p><a href="https://micronaut.io/guides/">Go to the linked site</a></p>]]></description><guid>micronaut-guides-micronaut-3</guid><pubDate>Wed, 18 Aug 2021 21:40:29 GMT</pubDate></item><item><title>Micronaut® framework 3 Released</title><link>https://sergiodelamo.com/blog/micronaut-3-released.html</link><description><![CDATA[<blockquote><p>A major release of the Framework has given us the opportunity to fix the design mistakes of the past and implement important changes to make the Framework more intuitive to use and adaptable to future requirements.</p></blockquote><p>The update should not be difficult. You will need to update to the <code>jakarta.inject</code> annotations and to the Micronaut nullability annotations. If you were RxJava2 code, we recommend you to update to Project Reactor but you can keep using RxJava 2 simply by adding a dependency.</p><p>There are improvements in every module but the DI and AOP improvements in core are really powerful:</p><ul><li>Injection by generics.</li><li>Qualifier annotations</li><li>Limit injectable types</li><li>AOP Interceptions for constructors and life cycle methods(<code>@PostConstruct</code>, ...)</li></ul><p><a href="https://micronaut.io/2021/08/18/micronaut-framework-3-released/">Go to the linked site</a></p>]]></description><guid>micronaut-3-released</guid><pubDate>Wed, 18 Aug 2021 21:08:51 GMT</pubDate></item><item><title>Automatically rearrange spaces based on most recent use</title><link>https://sergiodelamo.com/blog/automatically-rearrange-spaces-based-on-most-recent-use.html</link><description><![CDATA[<p>I enjoyed the <em>Mac Power Users</em> podcast episode, <a href="https://www.relay.fm/mpu/600">#600: Mac Migration &amp; Settings</a>, where <a href="https://www.macsparky.com">David Sparks</a> and <a href="https://512pixels.net">Stephen Hackett</a> covered MacOS settings.</p><p>I agree that <code>Automatically rearrange spaces based on most recent use</code>, which is checked by default, is probably the worst MacOS default setting.</p><p><img src="https://images.sergiodelamo.com/automatically-rearrange-spaces-based-on-most-recent-use.png" alt="Unchecked Automatically rearrange spaces based on most recent use MacOS setting" /></p>]]></description><guid>automatically-rearrange-spaces-based-on-most-recent-use</guid><pubDate>Wed, 18 Aug 2021 17:57:45 GMT</pubDate></item><item><title>Apple Podcast Smart Banner</title><link>https://sergiodelamo.com/blog/apple-podcasts-smart-banner.html</link><description><![CDATA[<p>You can use <a href="https://developer.apple.com/documentation/webkit/promoting_apps_with_smart_app_banners">Apple Smart Banners</a> not just for apps but also for podcasts.</p><blockquote><p>Smart App Banners vastly improve users’ browsing experience compared to other promotional methods. In iOS, Smart App Banners provide a consistent look and feel that users come to recognize. They trust that tapping the banner will take them to the App Store and not a third-party advertisement. They appreciate unobtrusive banners at the top of a webpage, instead of a full screen that interrupts their experience with the web content. And with a large and prominent Close button, a banner is easy to dismiss. When the user returns to the webpage, the banner doesn’t reappear.</p></blockquote><p><img src="https://images.sergiodelamo.com/codigobot-apple-podcasts-smart-banner.jpg" alt="Código Bot Podcast Smart Banner" /></p><p>You can obtain the <code>app-id</code>, a nine-digit unique identifier, from the Apple Podcast Landing page.</p><p><img src="https://images.sergiodelamo.com/codigobot-apple-podcasts-app-id.png" alt="Código Bot Podcast app-id" /></p><p>I added a HTML meta to the HTML head of <a href="https://codigobot.com">codigobot.com</a> pages.</p><pre><code class="language-html">...  &lt;head&gt;    &lt;meta name=&quot;apple-itunes-app&quot; content=&quot;app-id=1548425863&quot;&gt;    ....    ...    ..</code></pre><p>Subscribe to <a href="https://codigobot.com">Código Bot</a> with you podcast player of choice.</p>]]></description><guid>apple-podcasts-smart-banner</guid><pubDate>Sun, 15 Aug 2021 06:37:43 GMT</pubDate></item><item><title>&#x1f468;&#x1f3fb;‍&#x1f3eb; Teaching Micronaut Deep Dive</title><link>https://sergiodelamo.com/blog/micronaut-deep-dive-september-2021.html</link><description><![CDATA[<p>🗓 Dates: Sept. 13 to Sept. 16<br />⏰ Time: 9:00 a.m. to 12:00 p.m. CDT<br />📍 Location: Online<br />🧑‍🏫 Instructor: Yours truly<br />💵 Registration Fee: $199.00 USD</p><p><a href="https://objectcomputing.com/training/register/offering/298">Enroll Now</a></p><blockquote><p>The following topics are covered in this course.</p><ul><li>Introduction to the Micronaut® framework</li><li>Controllers</li><li>Compile-time dependency injection</li><li>Application configuration</li><li>Testing</li><li>HTTP client</li><li>Service discovery</li><li>Management endpoints</li><li>Static file resolution</li><li>Polyglot Micronaut</li><li>Micronaut Data</li><li>Introduction to serverless deployment</li><li>Stand-alone CLI apps</li></ul></blockquote><p>During the course, I teach by building an application and explaining the concepts. Don't expect slides but 12 hours of live coding.</p><p><a href="https://objectcomputing.com/services/training/catalog/micronaut-training/micronaut-deep-dive">Go to the linked site</a></p>]]></description><guid>micronaut-deep-dive-september-2021</guid><pubDate>Sat, 14 Aug 2021 07:31:05 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 015 - Libros Para el Verano</title><link>https://sergiodelamo.com/blog/codigobot015.html</link><description><![CDATA[<p>En estas fechas veraniegas, Kini y yo os recomendamos algunos de nuestros libros favoritos.</p><p><a href="https://codigobot.com/015.html"><img src="https://media.codigobot.com/artwork-landscape.png" alt="" /></a></p><p><a href="https://codigobot.com/015.html">Go to the linked site</a></p>]]></description><guid>codigobot015</guid><pubDate>Fri, 13 Aug 2021 08:54:39 GMT</pubDate></item><item><title>Books for the summer</title><link>https://sergiodelamo.com/blog/books-for-the-summer.html</link><description><![CDATA[<h1><a href="http://daniel-suarez.com/daemon10thsynopsis.html">Daemon</a></h1><p><img src="https://images.sergiodelamo.com/daemon-anniv-cover.jpg" alt="" /></p><blockquote><p>It all begins when one man's obituary appears online. . .Matthew Sobol was a legendary computer game designer—the architect behind half a dozen popular online games. His premature death from brain cancer depressed both gamers and his company’s stock price. But Sobol’s fans weren’t the only ones to note his passing. He left behind something that was scanning Internet obituaries, too—something that put in motion a whole series of programs upon his death. Programs that moved money. Programs that recruited people. Programs that killed.</p></blockquote><p>I have read every <a href="http://daniel-suarez.com">Daniel Suarez</a> book but <em>Delta-V</em>. He is one of my favourite authors. If your in a programmer, you will love Daemon.</p><h1><a href="https://itrevolution.com/the-phoenix-project/">The Phoenix Project</a></h1><p><img src="https://images.sergiodelamo.com/201874.84d9b6c1c2c7d24af31e3ff5eae4aef5.png" alt="" /></p><blockquote><p>Bill, an IT manager at Parts Unlimited, has been tasked with taking on a project critical to the future of the business, code named Phoenix Project. But the project is massively over budget and behind schedule. The CEO demands Bill must fix the mess in ninety days or else Bill’s entire department will be outsourced.</p></blockquote><blockquote><p>With the help of a prospective board member and his mysterious philosophy of The Three Ways, Bill starts to see that IT work has more in common with a manufacturing plant work than he ever imagined. With the clock ticking, Bill must organize work flow streamline interdepartmental communications, and effectively serve the other business functions at Parts Unlimited.</p></blockquote><blockquote><p>In a fast-paced and entertaining style, three luminaries of the DevOps movement deliver a story that anyone who works in IT will recognize. Readers will not only learn how to improve their own IT organizations, they’ll never view IT the same way again.</p></blockquote><p>I enjoyed  as well the sequel <a href="https://itrevolution.com/the-unicorn-project/">the Unicorn project</a>. If you are a programmer you will enjoy it.</p><h1><a href="https://www.amazon.com/Goal-Process-Ongoing-Improvement/dp/0884271951">The Goal</a></h1><p><img src="https://images.sergiodelamo.com/the-goal.jpg" alt="" /></p><blockquote><p>Written in a fast-paced thriller style, 'The Goal' contains a serious message for all managers in industry and explains the ideas which underline the Theory of Constraints developed by the author.</p></blockquote><p>Fantastic book. I am fascinated by the theory of constraints and how it can be applied to our processes.</p>]]></description><guid>books-for-the-summer</guid><pubDate>Thu, 12 Aug 2021 07:17:39 GMT</pubDate></item><item><title>Start Orbit Timers with AppleScript</title><link>https://sergiodelamo.com/blog/apple-script-orbit.html</link><description><![CDATA[<p>My <a href="https://www.getharvest.com/">Harvest</a> yearly subscription is coming to an end. Harvest lacks iPad OS and Watch OS applications. They don't have an iOS widget. Moreover, it seems they are not really committed to native applications for Apple Platforms.</p><p>I heard about <a href="https://timeinorbit.com">Orbit</a>, a time tracking app, in the latest episode of <a href="https://appstories.net/episodes/235/">App Stories Podcast</a>; an interview with <a href="https://twitter.com/malinsundberg">Malin Sundberg</a> creator of Orbit.</p><p>Unfortunately, Orbit is not scriptable. Neither is <a href="https://sergiodelamo.com/blog/tag/harvest.html">Harvest</a>.</p><p>However, I managed to write AppleScript scripts to automate things.</p><p>The following script stops the current timer</p><pre><code class="language-applescript">tell application &quot;System Events&quot;tell process &quot;Orbit&quot;	set frontmost to true	keystroke &quot;.&quot; using command downend tellend tell</code></pre><p>I leaverage Orbit's shortcut <code>CMD + .</code> to stop the current timer.</p><p>Starting a timer programmatically is more tricky. I had to do a some <code>set uiElems to entire contents</code> debugging 😓.</p><pre><code class="language-applescript">on run argvif (count of argv) &gt; 2 then	set theClient to item 1 of argv	set theProject to item 2 of argv	set theCategory to item 3 of argv	if (count of argv) &gt; 3 then		set theNote to item 4 of argv	else		set theNote to &quot;&quot;	end if	tell application &quot;System Events&quot;		tell process &quot;Orbit&quot;			set frontmost to true			keystroke &quot;n&quot; using command down			delay 0.1			set popupButtonName to &quot;Client:&quot;			set targetMenuItemName to theClient			tell pop up button popupButtonName of sheet 1 of window &quot;Orbit&quot;				click				set menuItemNames to name of every menu item of menu 1				repeat with i from 1 to (menuItemNames count)					set menuItemName to (item i of menuItemNames)					if menuItemName contains targetMenuItemName then						click menu item menuItemName of menu 1					end if				end repeat			end tell			delay 0.1			set popupButtonName to &quot;Project:&quot;			set targetMenuItemName to theProject			tell pop up button popupButtonName of sheet 1 of window &quot;Orbit&quot;				click				set menuItemNames to name of every menu item of menu 1				repeat with i from 1 to (menuItemNames count)					set menuItemName to (item i of menuItemNames)					if menuItemName contains targetMenuItemName then						click menu item menuItemName of menu 1					end if				end repeat			end tell			delay 0.1			set popupButtonName to &quot;Category:&quot;			set targetMenuItemName to theCategory			tell pop up button popupButtonName of sheet 1 of window &quot;Orbit&quot;				click				set menuItemNames to name of every menu item of menu 1				repeat with i from 1 to (menuItemNames count)					set menuItemName to (item i of menuItemNames)					if menuItemName contains targetMenuItemName then						click menu item menuItemName of menu 1					end if				end repeat			end tell			delay 0.1			if theNote is not &quot;&quot; then				set focused of text area 1 of scroll area 1 of sheet 1 of window &quot;Orbit&quot; of application process &quot;Orbit&quot; of application &quot;System Events&quot; to true				keystroke theNote				set focused of text area 1 of scroll area 1 of sheet 1 of window &quot;Orbit&quot; of application process &quot;Orbit&quot; of application &quot;System Events&quot; to false				delay 0.1			end if			click button &quot;Start&quot; of sheet 1 of window &quot;Orbit&quot; of application process &quot;Orbit&quot; of application &quot;System Events&quot;		end tell	end tellelse	set result to text returned of (display dialog &quot;You need three arguments to start Orbit&quot;)end ifend run</code></pre><p>I start timers for example from <a href="https://bunchapp.co">Bunch files</a></p><pre><code class="language-yaml">---title: 🔔 Github---........* ~/Library/Scripts/Orbit\ Start\ Timer.scpt &quot;OCI&quot; &quot;Micronaut Product Development&quot; &quot;Github&quot;</code></pre><p>See it in action:</p><p><img src="https://images.sergiodelamo.com/orbit-apple-script.gif" alt="AppleScript Orbit" /></p>]]></description><guid>apple-script-orbit</guid><pubDate>Wed, 11 Aug 2021 06:07:21 GMT</pubDate></item><item><title>Secret Rotation - Micronaut AWS Lambda and Secrets Manager</title><link>https://sergiodelamo.com/blog/micronaut-guide-aws-secretsmanager-rotation.html</link><description><![CDATA[<p>I wrote a <a href="https://guides.micronaut.io">Micronaut Guide</a> which shows how to develop a <a href="https://aws.amazon.com/lambda/">AWS Lambda</a> function with Micronaut® framework to rotate a JWK (JSON Web Key) saved into <a href="https://aws.amazon.com/secrets-manager/">AWS Secrets Manager</a>.</p><p>To get the most of this guide, read these guides as well:</p><ul><li><a href="https://guides.micronaut.io/latest/micronaut-cli-jwkgen.html">JW Generation with a Micronaut Command Line Application</a></li><li><a href="https://guides.micronaut.io/latest/micronaut-security-keys-jwks.html">JWK Keys endpoint</a></li><li><a href="https://guides.micronaut.io/latest/micronaut-aws-secretsmanager.html">Distributed Configuration with AWS Secrets Manager and the Micronaut® framework</a></li></ul><p><a href="https://guides.micronaut.io/latest/micronaut-aws-secretsmanager-rotation.html">Go to the linked site</a></p>]]></description><guid>micronaut-guide-aws-secretsmanager-rotation</guid><pubDate>Tue, 10 Aug 2021 16:47:37 GMT</pubDate></item><item><title>AppleScript Transmit File Sync</title><link>https://sergiodelamo.com/blog/apple-script-transmit-s3-sync.html</link><description><![CDATA[<p>I <a href="https://sergiodelamo.com/blog/static-website-hosting-with-aws.html">host this website's media in a S3 bucket</a>.Transmit handles FTP, SFTP, WebDAV but also cloud services such as S3, Google Drive, Dropbox, Microsoft Azure...</p><p>I use <a href="https://library.panic.com/transmit/transmit5/synchronize/">File Sync</a> in <a href="https://panic.com/transmit/">Transmit 5</a> to sync a local folder on my Mac with a S3 bucket.</p><blockquote><p>Synchronization is a quick, fully automated method of bringing a folder up to date with the contents of another folder.</p></blockquote><p>The name of the Transmit's favorite is <code>images.sergiodelamo.com</code>. I wrote an AppleScript to trigger the sync:</p><pre><code class="language-applescript">tell application &quot;Transmit&quot;    activate        set myFave to item 1 of (favorites whose name is &quot;images.sergiodelamo.com&quot;)        tell document 1        tell current tab            connect to myFave            synchronize local browser to remote browser            close remote browser        end tell    end tell        quitend tell</code></pre>]]></description><guid>apple-script-transmit-s3-sync</guid><pubDate>Sat, 07 Aug 2021 16:48:09 GMT</pubDate></item><item><title>Static Website Hosting with AWS</title><link>https://sergiodelamo.com/blog/static-website-hosting-with-aws.html</link><description><![CDATA[<p>I host this website with <a href="https://aws.amazon.com">AWS</a>. It is a static website which I generate with a <a href="https://gradle.org">Gradle</a> build.</p><p>This is the architecture diagram:</p><p><img src="https://images.sergiodelamo.com/static-website-aws-architecture-s3-cloudfront-certificate-manager-route53.png" alt="" /></p><h2>S3</h2><p>I have two <a href="https://aws.amazon.com/s3/">Amazon S3</a> buckets. One for the code (HTML, CSS...) and another bucket for the media (Images, Videos...)</p><h2>AWS Certificate Manager</h2><p>I created a certificate for the domain name with<a href="https://aws.amazon.com/certificate-manager/">AWS Certificate Manager</a>. The certificate covers the domain apex <code>sergiodelamo.com</code> and the wildcard <code>*.sergiodelamo.com</code></p><h2>CloudFront</h2><p>I have two <a href="https://aws.amazon.com/cloudfront/">Amazon CloudFront</a> distributions distributing the content of each bucket. One distribution for the code bucket <code>sergiodelamo.com</code> and another one for the media <code>images.sergiodelamo.com</code>. Both distributions use the certificate. One distribution uses the domain <code>sergiodelamo.com</code>, the other the domain <code>images.sergiodelamo.com</code>.</p><h2>Route53</h2><p>I manage the website domain name at <a href="https://www.hover.com">Hover</a>. I delegate DNS to <a href="https://aws.amazon.com/route53/">Amazon Route53</a>. In Route53, I have two DNS Records A which are alias to the CloudFront distributions.</p><p>Such an architecture is simpler than it looks like to create. I am really happy with it.</p>]]></description><guid>static-website-hosting-with-aws</guid><pubDate>Fri, 06 Aug 2021 06:53:06 GMT</pubDate></item><item><title>TextExpander Popup Menu</title><link>https://sergiodelamo.com/blog/text-expander-dropdown-macros.html</link><description><![CDATA[<p>We produce HTML pages for each <a href="https://guides.micronaut.io">Micronaut Guide</a>. However, we write them as plain text files (more precisely Asciidoc files). We have custom macros to include common snippets. For example, to generate a callout about an <code>@Introspected</code> annotation we write:</p><p><code>callout:introspected[1]</code></p><p>Automation is not just about saving time. It is a about correctness. I have created a <a href="https://textexpander.com/">Text Expander</a> snippet to help me write those callouts.</p><p>I trigger the snippet with <code>zcallout</code></p><p><img src="https://images.sergiodelamo.com/text-expander-callout.png" alt="" /></p><p>and it uses a Popup Menu:</p><p><img src="https://images.sergiodelamo.com/text-expander-popup-menu.png" alt="" /></p><p>this is how it works:</p><p><img src="https://images.sergiodelamo.com/CalloutMacro.gif" alt="" /></p>]]></description><guid>text-expander-dropdown-macros</guid><pubDate>Thu, 05 Aug 2021 09:41:50 GMT</pubDate></item><item><title>Avoid Infinity Pools with iOS Content Restrictions</title><link>https://sergiodelamo.com/blog/avoid-infinity-pools-with-ios-content-restrictions.html</link><description><![CDATA[<p>I first heard about Infinity Pools in the <a href="https://www.relay.fm/focused">Focused Podcast</a>. I think that it comes from the book <a href="https://maketime.blog">Make Time: A Friendly Approach to Focus and Energy</a></p><blockquote><p>Infinity Pools are always-on, effectively infinite sources of information and entertainment.</p></blockquote><p>For me, <a href="https://as.com">as.com</a> and <a href="https://marca.com">marca.com</a>, two sport websites, were huge infinity pools on my phone. I deleted the native apps but I often found myself checking those websites on Safari for no other reason than habit. I want to focus my attention to other apps (e.g. <a href="https://netnewswire.com">NetNewsWire</a>) or better not use the phone and be more present.</p><p>iOS has a setting <code>Screen Time -&gt; Tap Content &amp; Privacy Restrictions -&gt; Content Restrictions -&gt; Web Content</code> whose primary use is to limit adult content but you can block individual websites as well:</p><p><img src="https://images.sergiodelamo.com/infinity-pools-ios-screen-time-content-restrictions-web-content.png" alt="iOS Web content restrictions" /></p>]]></description><guid>avoid-infinity-pools-with-ios-content-restrictions</guid><pubDate>Wed, 04 Aug 2021 20:13:15 GMT</pubDate></item><item><title>Compose Tweet about a website with Shortcuts</title><link>https://sergiodelamo.com/blog/shortcuts-tweet-compose.html</link><description><![CDATA[<p>The following Javascript snippet composes a Tweet.</p><ul><li>It fetches the title <code>&lt;meta name=&quot;description&quot;</code> of a page.</li><li>It does some replacements to mention some twitter profiles.</li><li>It uses the <code>&lt;meta name=&quot;keywords&quot;</code> as a list of <a href="https://help.twitter.com/en/using-twitter/how-to-use-hashtags">hashtags</a>.</li><li>It appends the url of the website to the tweet.</li></ul><pre><code class="language-javascript">function getMeta(metaName) {  var metas = document.getElementsByTagName('meta');  for (let i = 0; i &lt; metas.length; i++) {    if (metas[i].getAttribute('name') === metaName) {      return metas[i].getAttribute('content');    }  }  return '';}var tweet = document.title + '. ' + getMeta('description');const replacements = new Map();replacements.set('Código Bot', '@codigobotfm');replacements.set('Micronaut', '@micronautfw');replacements.set('Grails', '@grailsfw');for (const [key, value] of replacements) {	tweet = tweet.replace(key, value)}tweet += ' #' + getMeta('keywords').replace(',', ' #');tweet += window.location.href;</code></pre><p>Next, create a <a href="https://support.apple.com/guide/shortcuts/welcome/ios">Shortcut</a>.</p><blockquote><p>A shortcut is a quick way to get one or more tasks done with your apps. The Shortcuts app lets you create your own shortcuts with multiple steps.</p></blockquote><p>It is a simple three step shortcut:</p><ul><li>It accepts <em>Safari web pages</em></li><li>Run a Javascript snippet. Copy the above snippet. Add a last line of code <code>completion(tweet);</code></li><li>Open <a href="https://tapbots.com/tweetbot">Tweetbot</a> to specified account.</li></ul><p><img src="https://images.sergiodelamo.com/shortcut-accepts-safari-web-pages-run-javascript-tweetbot.jpg" alt="Shortcuts - Accept Safari Web Page - Run Javascript - Tweet" /></p>]]></description><guid>shortcuts-tweet-compose</guid><pubDate>Tue, 03 Aug 2021 06:44:54 GMT</pubDate></item><item><title>Compose an email with AppleScript</title><link>https://sergiodelamo.com/blog/mail-with-apple-script.html</link><description><![CDATA[<p>I have recently committed to send every Friday an update email to someone at work. Few lines need change from week to week. I have written a simple AppleScript script to help me do it.</p><pre><code class="language-applescript">set recipientName to &quot;John Snow&quot;set recipientAddress to &quot;jsnow@gameofthrones.example&quot;set theSubject to &quot;Winter is Comming&quot;set theContent to &quot;Hi I have seen white walkers in the following spots: XXXThanks,Sergio&quot;tell application &quot;Mail&quot;        set theMessage to make new outgoing message with properties {subject:theSubject, content:theContent, visible:true}        tell theMessage        make new to recipient with properties {name:recipientName, address:recipientAddress}    end tellend tell</code></pre><p>Not every detail of the email is completely automated. Hence, I need to fill some details before clicking send.</p><p>If you have every detail ready, you can send the emails directly from <code>AppleScript</code> with the <code>send</code> keyword.</p>]]></description><guid>mail-with-apple-script</guid><pubDate>Mon, 02 Aug 2021 07:06:46 GMT</pubDate></item><item><title>AppleScript to stop Harvest Timer</title><link>https://sergiodelamo.com/blog/applescript-javascript-to-stop-current-harvest-timer.html</link><description><![CDATA[<p>I use <a href="https://harvestapp.com">Harvest</a> for time tracking.</p><p>Also, I use <a href="https://bunchapp.co">Bunch</a>. Almost all of my bunches start a Harvest timer. I want to stop the current timer when I close the bunch.</p><p>This is an excerpt of my <em>Blog</em> bunch.</p><pre><code>......* ~/Library/Scripts/Harvest.scpt &quot;SAC&quot; &quot;Writing&quot; &quot;Writing post&quot;  ~5!&lt;&lt;#On Close___#[On Close]* ~/Library/Scripts/Stop\ Harvest.scpt</code></pre><p>Unfortunately, Harvest does not integrate with AppleScript. Moreover, they don't have a menu option to stop the current timer. Neither, a <a href="https://support.getharvest.com/hc/en-us/articles/360048180272-Mac-App-Keyboard-Shortcuts">Keyboard Shortcut</a> to stop the current timer. You can stop the the timer by click the <code>H</code> in the menu item but I am not sure I an automate that.</p><p>Thus, I resort to browser automation. The following AppleScript:</p><ul><li>Opens Safari</li><li>Creates new tab and navigates to my harvest account.</li><li>Clicks the current timer stop button via Javascript.</li></ul><pre><code class="language-applescript">set website to &quot;https://softamo.harvestapp.com/time&quot;set theScript to &quot;document.getElementsByClassName('js-stop-timer')[0].click();&quot;tell application &quot;Safari&quot;    tell window 1        set current tab to (make new tab with properties {URL:website})    end tell    delay 5    do JavaScript theScript in current tab of first windowend tell</code></pre>]]></description><guid>applescript-javascript-to-stop-current-harvest-timer</guid><pubDate>Sun, 01 Aug 2021 06:17:24 GMT</pubDate></item><item><title>Mail Shortcut to Export to PDF</title><link>https://sergiodelamo.com/blog/macos-mail-export-to-pdf-shortcut.html</link><description><![CDATA[<p>It is easy to create a shortcut to a menu item of an application.</p><p><img src="https://images.sergiodelamo.com/preferences-keyboard-app-shortcuts-mail-export-as-pdf.png" alt="" /></p><p>Thanks to <a href="https://folivora.ai">BetterTouchTool</a>, I use <a href="https://docs.folivora.ai/docs/1004_hyper_key.html">Caps Lock as Hyper Key</a>.</p><blockquote><p>A Hyper Key is a magical key which automatically presses all the standard modifiers (ctrl+shift+cmd+opt).</p></blockquote><p>Thus, to generate a PDF, I select the email and type <em>Caps Lock + P</em>.</p>]]></description><guid>macos-mail-export-to-pdf-shortcut</guid><pubDate>Sat, 31 Jul 2021 05:08:35 GMT</pubDate></item><item><title>Automatically generate a PDF from a Screenshot</title><link>https://sergiodelamo.com/blog/generate-a-pdf-from-a-screenshot-automatically-with-hazel-and-automator.html</link><description><![CDATA[<p>It is easy to <a href="https://support.apple.com/en-us/HT201361">take a screenshot on your Mac</a>. Everyday, I use <code>Shift + Command + 4</code>.</p><p>I often want to generate a PDF from the Screenshot. For example, it is the screenshot of an invoice which I want to file as PDF.</p><p>With <a href="https://support.apple.com/guide/automator/welcome/mac">Automator</a> you can create a <em>Quick Action</em> to help you do that:</p><blockquote><p>Quick Actions are workflows that may be added to Finder, Touch Bar and the Services menu. You can manage Quick Actions in System Preferences.</p></blockquote><p><img src="https://images.sergiodelamo.com/automator-quickaction.png" alt="Automator Quick Action" /></p><p>Automator ships with a built-in action to generate a PDF from an image:</p><p><img src="https://images.sergiodelamo.com/automator-new-pdf-from-images.png" alt="" /></p><p>MacOS saves screenshots by default to the Desktop. Their name starts with <em>Screenshot</em>.</p><blockquote><p>By default, screenshots save to your desktop with the name ”Screen Shot [date] at [time].png.”</p></blockquote><p>You end up with something like <code>Screenshot 2021-07-29 at 13.20.29.png</code>.</p><p>Next, I use <a href="https://www.noodlesoft.com">Hazel</a> to monitor by <em>Desktop</em> folder.</p><blockquote><p>Hazel watches whatever folders you tell it to, automatically organizing your files according to the rules you create</p></blockquote><p>When I take a Screenshot, the Screenshot app saves a new image to the Desktop. The Hazel's rule checks if the new Desktop file is image and whether its name starts with <em>Screenshot</em>. If it does, it invokes the Automator Quick Action to generate a PDF.</p><p><img src="https://images.sergiodelamo.com/hazel-run-automator-workflow.png" alt="Run an Automator Workflow from Hazel" /></p><p>To finish, it displays a notification as the icing on the cake.</p>]]></description><guid>generate-a-pdf-from-a-screenshot-automatically-with-hazel-and-automator</guid><pubDate>Fri, 30 Jul 2021 09:17:09 GMT</pubDate></item><item><title>The thriving Mac business</title><link>https://sergiodelamo.com/blog/mac-thriving-business.html</link><description><![CDATA[<p><a href="https://www.apple.com/investor/earnings-call/">Apple Financial Results - Q3 2021 - July 27, 2021</a>:</p><blockquote><p>It is remarkable that the last four quarters for Mac have been its best four quarters ever. This exceptional level of sales success has been driven by the very enthusiastic customer response to our new Macs powered by the M1 chip, which we most recently brought to our newly redesigned iMac.</p></blockquote><p><a href="https://sixcolors.com/post/2021/07/apple-posts-81b-quarterly-results-charts/">Sixcolors Q3 2021 Charts</a> show the Mac momentum.</p><figure><img src='https://images.sergiodelamo.com/financials-2021-7-1-1-6c.png.webp' alt='Sixcolors Mac revenue Q3 2021'/><figcaption>Source: <a href="https://sixcolors.com/post/2021/07/apple-posts-81b-quarterly-results-charts/">Sixcolors Mac revenue Q3 2021</a></figcaption></figure><p>Half a year later, what I said about the <a href="https://sergiodelamo.com/blog/macbook-air-m1.html">Macbook Air M1</a> stays true. And, it is translating into record sales.</p><blockquote><p>It does not get hot. It is quiet. It is fast. It is the future. It is now.</p></blockquote><p><a href="https://www.apple.com/investor/earnings-call/">Go to the linked site</a></p>]]></description><guid>mac-thriving-business</guid><pubDate>Thu, 29 Jul 2021 07:58:35 GMT</pubDate></item><item><title>Run a Gradle build when you save in BBEdit</title><link>https://sergiodelamo.com/blog/bbedit-apple-script-document-did-save.html</link><description><![CDATA[<p>My blog is a static website which I generate with a <a href="http://gradle.org">Gradle</a> build. Gradle builds the website if I run:</p><p><code>cd ~/github/sdelamo/sergiodelamo.com/;./gradlew build</code></p><p>My posts are Markdown files under <code>~/github/sdelamo/sergiodelamo.com/posts</code></p><p>I want <a href="https://www.barebones.com/products/bbedit/">BBEdit</a> to run my Gradle build when I save a Markdown file in my blog's folder. Thanks to BBEdit's AppleScript integration it is possible.</p><p>I created an AppleScript in the <em>Attachment Scripts</em> folder:</p><blockquote><p>The Attachment Scripts folder contains AppleScripts which are run at specific points: when BBEdit starts or quits; and when documents are open, saved, and closed.</p></blockquote><p>I saved the following <em>AppleScript</em> script at <code>~/Library/Application Support/BBEdit/Attachment Scripts/Document.documentDidSave.scpt</code>.</p><p>It runs when I save a blog post's Markdown file:</p><pre><code class="language-applescript">on documentDidSave(doc)    set p to file of doc    if (p as text) ends with &quot;.md&quot; and (p as text) contains &quot;github:sdelamo:sergiodelamo.com&quot; then        set buildScript to &quot;cd ~/github/sdelamo/sergiodelamo.com/;./gradlew build&quot;        tell application &quot;Terminal&quot;            if (exists window 1) and not busy of window 1 then                do script buildScript in window 1            else                do script buildScript            end if        end tell    end ifend documentDidSave</code></pre><p>This script allows me to focus on writing.</p>]]></description><guid>bbedit-apple-script-document-did-save</guid><pubDate>Wed, 28 Jul 2021 09:47:51 GMT</pubDate></item><item><title>Apple Photos - Multiple Libraries</title><link>https://sergiodelamo.com/blog/photos-choose-library.html</link><description><![CDATA[<p>You can create multiple <a href="https://www.apple.com/macos/photos/">Photos for macOS</a> libraries.</p><p>Click the Photos icon while holding the <em>Option ⌥</em> Key</p><p><img src="https://images.sergiodelamo.com/photos-choose-library.jpg" alt="Photos for MacOs choose a Library" /></p>]]></description><guid>photos-choose-library</guid><pubDate>Mon, 26 Jul 2021 19:02:12 GMT</pubDate></item><item><title>21 Apps for 2021 - Amphetamine</title><link>https://sergiodelamo.com/blog/macos_amphetamine.html</link><description><![CDATA[<p>My favourite podcast of 2020 was <a href="https://sixcolors.com/post/2020/08/20-macs-for-2020-an-introduction/">20 Macs for 2020</a></p><blockquote><p>With this year marking the turn of decades (in some particularly disastrous ways, as it turns out), I decided to construct a list of the 20 most notable Macs in history. Over the next 20 weeks, I’ll post essays, podcasts, and videos about each of them, counting down to number one</p></blockquote><p>In a similar flavour, I am going to write a list of my favourite <em>21 MacOS applications in 2021</em>.</p><p>To start <em>21 apps for 2021</em>, let me point you to <a href="https://apps.apple.com/app/amphetamine/id937984704?mt=12">Amphetamine</a>, a keep-awake utility, by <a href="https://twitter.com/x74353">William Gustafson</a>.</p><p>Often, I need to run a long builds in my computer.Moreover, I want to keep it awake while the tests run. For example, I am currently running the test suite of <a href="https://github.com/micronaut-projects/micronaut-guides/pulls">Micronaut Guides</a> in preparation for <a href="https://micronaut.io">Micronaut 3 release</a>.</p><p>I find myself often starting an Amphetamine session which should last while the Terminal is running:</p><p><img src="https://images.sergiodelamo.com/amphetamine-session-while-terminal-is-running.png" alt="amphetamine session while terminal is running" /></p><p>If you ever need to keep your computer awake, Amphetamine is the app you have been looking for.</p><p><a href="https://apps.apple.com/app/amphetamine/id937984704?mt=12">Go to the linked site</a></p>]]></description><guid>macos_amphetamine</guid><pubDate>Mon, 26 Jul 2021 15:53:01 GMT</pubDate></item><item><title>Submit your podcast to Apple Podcasts Connect</title><link>https://sergiodelamo.com/blog/apple-podcasts-connect-and-the-itunes-api.html</link><description><![CDATA[<p>It makes your podcast appear not just in Apple Podcasts but also in the iTunes API</p><p>Submit your podcast to <a href="https://podcastsconnect.apple.com/">Apple Podcast Connect</a>. It makes your podcast appear not just in <a href="https://podcastsconnect.apple.com/">Apple Podcasts</a> but also in the iTunes API which is key to appear in almost every third-party client.</p><p><a href="https://atp.fm/427">Accidental Tech Podcast <a href="https://sergiodelamo.com/blog/tag/427.html"><span class="hashtag">#427</span></a></p><video controls src='https://images.sergiodelamo.com/atp-itunes-api.mp4'/>]]></description><guid>apple-podcasts-connect-and-the-itunes-api</guid><pubDate>Sun, 25 Jul 2021 09:27:22 GMT</pubDate></item><item><title>How to distribute a Podcast to YouTube</title><link>https://sergiodelamo.com/blog/how-to-distribute-a-podcast-to-youtube.html</link><description><![CDATA[<p>The best way to enjoy a Podcast is to listen to it in a Podcast player - <a href="https://overcast.fm">Overcast</a>, <a href="https://castro.fm">Castro</a>, <a href="https://www.pocketcasts.com">Pocket Casts</a>. You enjoy features such as audio boost, clip sharing, illustrated MP3 chapters, show notes with links etc.</p><p>However, Youtube is such a huge distribution platform that you may want to distribute your podcast there as well.</p><p>I have struggled with this decision. YouTube is a closed platform with a lot of issues. Moreover, I don't want YouTube to replace the open Podcast ecosystem.</p><p>However, we were already distributing <a href="https://codigobot.com">Código Bot</a> to <a href="https://open.spotify.com/show/4KcAbjtZZEC5UnVVAGUovW">Spotify</a>, which one may argue is the biggest threat to the open Podcast ecosystem.</p><blockquote><p><a href="https://www.wsj.com/articles/spotify-strikes-exclusive-podcast-deal-with-joe-rogan-11589913814">Joe Rogan's full library, dating back 11 years, is to hit the service Sept. 1, and become exclusive to Spotify after that, before the end of the year</a></p></blockquote><p>Hence, I have decided to distribute <a href="https://www.youtube.com/channel/UCJE-j5dRWMDvz72V9dmIZRw">Código Bot to a Youtube channel</a>.</p><p>Checklist:</p><ul><li>I created a Youtube account with an email address dedicated to the podcast.</li><li>I created the channel once logged into Youtube.</li><li>I <a href="https://www.youtube.com/verify">verified the account</a> to be able to upload videos longer than 15 minutes.</li><li>I generated one video per mp3. I used <a href="https://fusioncast.rambo.codes/">FusionCast</a>, a MacOS app to turn your podcast episodes into videos for publishing on any platform.</li><li>Upload the videos and provide some metadata (I filled the date of recording to match the podcast date, the language, title, description and added every video to the podcast).</li></ul><p>If you prefer/need to use YouTube to consume your podcast, check <a href="https://www.youtube.com/channel/UCJE-j5dRWMDvz72V9dmIZRw">Código Bot's Youtube channel</a>.</p>]]></description><guid>how-to-distribute-a-podcast-to-youtube</guid><pubDate>Sat, 24 Jul 2021 06:21:25 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 014 - Apps y servicios Web (2)</title><link>https://sergiodelamo.com/blog/codigobot014.html</link><description><![CDATA[<p>2ª parte donde continuo hablando con Kini sobre las aplicaciones que uso en el día a día.</p><p>Hablamos de:</p><ul><li><a href="https://flexibits.com/fantastical">Fantastical</a></li><li><a href="https://www.omnigroup.com/omnifocus">Omnifocus</a></li><li><a href="https://nova.app">Nova</a></li><li><a href="https://netnewswire.com">NetNewsWire</a></li><li><a href="https://panic.com/transmit/">Transmit</a></li><li><a href="https://pdfexpert.com">PDF Expert</a></li></ul><p><a href="https://codigobot.com/014.html">Ir a la Página del Podcast</a></p><p><a href="https://codigobot.com/014.html">Go to the linked site</a></p>]]></description><guid>codigobot014</guid><pubDate>Fri, 23 Jul 2021 12:32:23 GMT</pubDate></item><item><title>AppleScript - Export multiple Word documents as PDFs</title><link>https://sergiodelamo.com/blog/apple-script-export-multiple-words-documents-to-pdf.html</link><description><![CDATA[<p>I was recently asked to review a Book which was shared with me as a series of Word documents. However, I would like them as PDF. I would like to read and annotate them with an iPad and <a href="https://pdfexpert.com">PDF Expert</a>.</p><p>AppleScript to the rescue!. The following AppleScript prompts you to select multiple word documents, asks you for a folder to save the PDFs, opens each document in Word and saves it as PDF.</p><p>The AppleScript script prompts you to select multiple word documents, asks you for a folder to save the PDFs, opens each document in Word and saves it as PDF.</p><pre><code class="language-applescript">tell application &quot;Microsoft Word&quot;set wordDocuments to choose file with prompt &quot;Please select the document to process:&quot; with multiple selections allowedset exportLocation to choose folder with prompt &quot;Please select where you'd like export the pdfs:&quot;repeat with a from 1 to number of wordDocuments	set doc to open item a of wordDocuments	set theActiveDoc to the active document	set docName to name of theActiveDoc	save as theActiveDoc file format format PDF file name ((exportLocation as text) &amp; docName &amp; &quot;.pdf&quot;)	close theActiveDocend repeatquitend tell</code></pre>]]></description><guid>apple-script-export-multiple-words-documents-to-pdf</guid><pubDate>Wed, 21 Jul 2021 15:23:19 GMT</pubDate></item><item><title>IntelliJ IDEA - Disable wildcard imports to always import single classes</title><link>https://sergiodelamo.com/blog/intellij-idea-disable-wildcard-imports.html</link><description><![CDATA[<p>keywords:idea</p><p><a href="https://www.jetbrains.com/help/idea/creating-and-optimizing-imports.html#disable-wildcard-imports">IntelliJ IDEA Documentation</a>:</p><blockquote><p>In the Settings/Preferences dialog ⌘,, select Editor | Code Style | Java | Imports.</p><ul><li>Make sure that the Use single class import option is enabled.</li><li>In the Class count to use import with '<em>' and Names count to use static import with '</em>' fields, specify values that definitely exceed the number of classes in a package and the number of names in a class (for example, 999 ).</li></ul></blockquote><p>It is possible to change a wildcard import:</p><blockquote><p>To replace an import statement with single class imports in a file without changing the settings, place the caret at the import statement, press ⌥⏎ (or use the intention action the Intention action icon icon), and select Replace with single class imports.</p></blockquote><p><a href="https://www.jetbrains.com/help/idea/creating-and-optimizing-imports.html#disable-wildcard-imports">Go to the linked site</a></p>]]></description><guid>intellij-idea-disable-wildcard-imports</guid><pubDate>Tue, 20 Jul 2021 08:51:47 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 013 - Apps y servicios Web (1)</title><link>https://sergiodelamo.com/blog/codigobot013.html</link><description><![CDATA[<p><img src="https://media.codigobot.com/artwork-864.png" alt="" /></p><p><a href="https://sergiodelamo.com">Sergio del Amo</a> y <a href="https://kinisoftware.com">Kini</a> sobre las aplicaciones y servicios Web que usamos en nuestro día a día.</p><p>Si usais un cliente de Podcast con soporte para capitulos en MP3, por ejemplo <a href="https://overcast.fm">Overcast</a>, encontrareis enlaces las aplicaciones que mencionamos.</p><p><img src="https://images.sergiodelamo.com/codigobot013-mp3chaters.jpg" alt="" /></p><p><a href="https://codigobot.com/013.html">Ir a la Página del Podcast</a></p><p><a href="https://codigobot.com/013.html">Go to the linked site</a></p>]]></description><guid>codigobot013</guid><pubDate>Fri, 09 Jul 2021 16:22:46 GMT</pubDate></item><item><title>Command Line JWK generation with Picocli and Micronaut® framework</title><link>https://sergiodelamo.com/blog/micronaut-cli-jwkgen.html</link><description><![CDATA[<p>I wrote a tutorial which shows how to write a command line application with <a href="https://picocli.info/">Picocli</a> and <a href="https://micronaut.io">Micronaut® framework</a> to generate a <a href="https://datatracker.ietf.org/doc/html/rfc7517">JWK (Json Web Key)</a>. It delegates to <a href="https://connect2id.com/products/nimbus-jose-jwt">Nimbus Jose + JWT</a>. I like JWKs and it is nice to have a command line util to generate them.</p><p><a href="https://guides.micronaut.io/latest/micronaut-cli-jwkgen.html">Read the tutorial</a></p><p><a href="https://guides.micronaut.io/latest/micronaut-cli-jwkgen.html">Go to the linked site</a></p>]]></description><guid>micronaut-cli-jwkgen</guid><pubDate>Sat, 12 Jun 2021 16:05:02 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 012</title><link>https://sergiodelamo.com/blog/codigobot012.html</link><description><![CDATA[<p>Hablo con <a href="https://kinisoftware.com">Kini</a> sobre su Alexa Skill <a href="https://alexa-skills.amazon.es/apis/custom/skills/amzn1.ask.skill.97912c7c-2622-4c3a-b2dc-cf163d961092/launch">Pizza Casera</a> donde el Chef <a href="https://davidguibertchef.com">David Guibert</a> te enseña a hacer Pizza.</p><p><a href="https://codigobot.com/012.html">Ir a la Página del Podcast</a></p><p><a href="https://codigobot.com/012.html">Go to the linked site</a></p>]]></description><guid>codigobot012</guid><pubDate>Fri, 11 Jun 2021 17:04:33 GMT</pubDate></item><item><title>ApacheCon 2021 - Static website generation with Apache Groovy</title><link>https://sergiodelamo.com/blog/apachecon-2021-static-website-with-apache-groovy.html</link><description><![CDATA[<p>I will be talking next September 21-23 about static website generation.</p><p>Very excited about this talk. This website is built using Apache Groovy and Gradle.</p><p><a href="https://www.apachecon.com/acah2021/"><img src="https://images.sergiodelamo.com/apachecon2021-static-websites-with-groovy-sergiodelamo.jpg" alt="" /></a></p><p><a href="https://www.apachecon.com/acah2021/">Go to the linked site</a></p>]]></description><guid>apachecon-2021-static-website-with-apache-groovy</guid><pubDate>Wed, 09 Jun 2021 06:38:39 GMT</pubDate></item><item><title>Micronaut Data JDBC</title><link>https://sergiodelamo.com/blog/micronaut-data-jdbc.html</link><description><![CDATA[<p>I wrote a tutorial which shows how to access a database with Micronaut Data JDBC. Compile-time implemented repositories make you really productive.</p><p><a href="https://guides.micronaut.io/latest/micronaut-data-jdbc-repository.html">Read the tutorial</a></p><p><a href="https://guides.micronaut.io/latest/micronaut-data-jdbc-repository.html">Go to the linked site</a></p>]]></description><guid>micronaut-data-jdbc</guid><pubDate>Mon, 31 May 2021 18:32:40 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 011</title><link>https://sergiodelamo.com/blog/codigobot-011.html</link><description><![CDATA[<p>Segunda parte de mi conversación con <a href="https://kinisoftware.com">Kini</a> sobre los cambios en Basecamp.</p><p>Hemos dividido en dos partes:</p><ul><li><a href="https://codigobot.com/011.html">Parte 2</a></li><li><a href="https://codigobot.com/010.html">Parte 1</a></li></ul><p><a href="https://codigobot.com/011.html">Go to the linked site</a></p>]]></description><guid>codigobot-011</guid><pubDate>Sat, 29 May 2021 14:02:42 GMT</pubDate></item><item><title>Micronaut AWS Lambda and a Cron job</title><link>https://sergiodelamo.com/blog/micronaut-aws-lambda-eventbridge-5minute-cron.html</link><description><![CDATA[<p>I wrote a tutorial which shows how to use scheduled expression with Event Bridge trigger to execute a Micronaut AWS Lambda every 5 minutes.</p><p><a href="https://guides.micronaut.io/latest/micronaut-aws-lambda-eventbridge-event">Read the tutorial</a></p><p><a href="https://guides.micronaut.io/latest/micronaut-aws-lambda-eventbridge-event">Go to the linked site</a></p>]]></description><guid>micronaut-aws-lambda-eventbridge-5minute-cron</guid><pubDate>Sat, 29 May 2021 08:32:40 GMT</pubDate></item><item><title>Generate thumbnails with Micronaut AWS Lambda and a S3 Notification trigger</title><link>https://sergiodelamo.com/blog/micronaut-guide-aws-lambda-s3-event-thumbnails-generation.html</link><description><![CDATA[<p>I wrote a tutorial which shows how to generate thumbnails for images uploaded to a S3 bucket with AWS Lambda and Micronaut. It uses <a href="https://github.com/coobird/thumbnailator">Thumbnailator</a> - a thumbnail generation library for Java.</p><p><a href="https://guides.micronaut.io/latest/micronaut-aws-lambda-s3-event">Read the guide</a></p><p><a href="https://guides.micronaut.io/latest/micronaut-aws-lambda-s3-event">Go to the linked site</a></p>]]></description><guid>micronaut-guide-aws-lambda-s3-event-thumbnails-generation</guid><pubDate>Sat, 29 May 2021 08:26:18 GMT</pubDate></item><item><title>CodePipeline for a Docker image of the Micronaut application GraalVM Native Image</title><link>https://sergiodelamo.com/blog/micronaut-graalvm-gradle-aws-codepipeline-codebuild-ecr-elasticbeanstalk-docker.html</link><description><![CDATA[<p>Continuous deployment - CodePipeline: Github → CodeBuild → GraalVM Native Image  → ECR → Elastic Beanstalk</p><p><img src="https://images.sergiodelamo.com/codepipeline-github-codebuild-graalvm-ecr-elasticbeanstalk.svg" alt="" /></p><p>Micronaut is build agnostic, you can use either Gradle or Maven. However, Gradle is my build tool of choice.</p><h2>Changes to Gradle Build</h2><h3>Generate Dockerrun.aws.json</h3><p>Create a template for <code>Dockerrun.aws.json</code></p><blockquote><p>A <code>Dockerrun.aws.json</code> file describes how to deploy a remote Docker image as an Elastic Beanstalk application. This JSON file is specific to Elastic Beanstalk. If your application runs on an image that is available in a hosted repository, you can specify the image in a Dockerrun.</p></blockquote><p><em>Dockerrun.aws.json</em></p><pre><code class="language-json">{    &quot;AWSEBDockerrunVersion&quot;: &quot;1&quot;,    &quot;Image&quot;: {        &quot;Name&quot;: &quot;@awsAccountId@.dkr.ecr.@awsRegion@.amazonaws.com/@awsEcrRepositoryName@:@imageTag@&quot;,        &quot;Update&quot;: &quot;true&quot;    },    &quot;Ports&quot;: [        {            &quot;ContainerPort&quot;: &quot;5000&quot;        }    ]}    </code></pre><p>I define the properties in <code>gradle.properties</code></p><pre><code class="language-properties">projectVersion=0.0.5...awsAccountId=XXXXXXXawsRegion=us-east-1awsEcrRepositoryName=YYYYY</code></pre><p>Create a Gradle tasks to populate the JSON template:</p><p><em>build.gradle</em></p><pre><code class="language-groovy">import org.apache.tools.ant.filters.ReplaceTokenstask copyAndReplaceTokensFromDockerrun(type: Copy) {    from('Dockerrun.aws.json') {        include 'Dockerrun.aws.json'        filter(ReplaceTokens, tokens: [imageTag: projectVersion,                                       awsAccountId: awsAccountId,                                       awsRegion: awsRegion,                                       awsEcrRepositoryName: awsEcrRepositoryName])    }    into 'build/docker'}</code></pre><h3>Configure Container registry</h3><p>Configure the <a href="https://aws.amazon.com/ecr/">Amazon ECR</a> repository as the container registry:</p><p><em>build.gradle</em></p><pre><code class="language-groovy">version = projectVersion......dockerBuildNative {  images = [&quot;${awsAccountId}.dkr.ecr.${awsRegion}.amazonaws.com/${awsEcrRepositoryName}:$project.version&quot;]}</code></pre><h2>AWS CodeBuild</h2><p>When you create your CodeBuild project, in the environment section, check:</p><blockquote><p>[x] Enable this flag if you want to build Docker images or want your builds to get elevated privileges`</p></blockquote><h3>CodeBuild Memory</h3><p>GraalVM Native Image generation is a memory intensive operation.</p><p>Select <code>7 GB memory, 4 vCPUs</code> instead of the default <code>3 GB memory, 2 vCPUs</code>.</p><h3>BuildSpec</h3><p>Create <a href="https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html"><code>buildspec.yml</code></a> file:</p><blockquote><p>A buildspec is a collection of build commands and related settings, in YAML format, that CodeBuild uses to run a build.</p></blockquote><pre><code class="language-yaml">version: 0.2phases:  install:    runtime-versions:      java: corretto11   pre_build:    commands:      - echo Logging in to Amazon ECR...      - $(aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION)  build:    commands:      - ./gradlew dockerPushNative      - ./gradlew copyAndReplaceTokensFromDockerrun  post_build:    finally:      - rm -f  /root/.gradle/caches/modules-2/modules-2.lock      - rm -fr /root/.gradle/caches/*/plugin-resolution/artifacts:  files:    - 'Dockerrun.aws.json'  base-directory: 'build/docker'cache:  paths:    - '/root/.gradle/caches/**/*'     - '/root/.gradle/wrapper/**/*'</code></pre><ul><li><code>dockerPushNative</code>, a task provided by the <a href="https://github.com/micronaut-projects/micronaut-gradle-plugin">Micronaut Gradle Plugin</a>, pushes a Docker Image built with GraalVM Native Image of the Micronaut application to ECR, the configured container registry</li></ul><p>The build's artifact is the output of the Gradle <code>copyAndReplaceTokensFromDockerrun</code> task.</p><h3>AWS Code Build IAM Role</h3><p>Attach a policy to the IAM role used by <a href="https://aws.amazon.com/codebuild/">AWS CodeBuild</a> to allow the build to publishing a new image to a <a href="https://aws.amazon.com/ecr/">Amazon ECR</a> repository. For example, the AWS Managed policy <code>AmazonEC2ContainerRegistryPowerUser</code>.</p><h2>Elastic Beanstalk</h2><p>Create a <code>Web server environment</code>. As platform select <code>Docker running on a 64bit Amazon Linux 2</code>.</p><h3>AWS Elastic Beanstalk IAM Role</h3><p>Attach a policy to the EC2 IAM role used by <a href="https://aws.amazon.com/elasticbeanstalk/">AWS Elastic Beanstalk</a> to allow the environment to retrieve the image from <a href="https://aws.amazon.com/ecr/">Amazon ECR</a> repository. For example, the AWS Managed policy <code>AmazonEC2ContainerRegistryReadOnly</code>.</p><h3>Start Micronaut Application in port 5000</h3><p>Set environment variable <code>MICRONAUT_SERVER_PORT</code> with value <code>5000</code> in <a href="https://aws.amazon.com/elasticbeanstalk/">AWS Elastic Beanstalk</a> <code>Configuration → Software</code>.</p><h2>AWS CodePipeline</h2><p><a href="https://aws.amazon.com/codepipeline/">AWS CodePipeline</a>:</p><ul><li>Source: Github (Version 2)</li><li>Build Provider: AWS CodeBuild</li><li>Deploy Provider: AWS Elastic Beanstalk</li></ul>]]></description><guid>micronaut-graalvm-gradle-aws-codepipeline-codebuild-ecr-elasticbeanstalk-docker</guid><pubDate>Tue, 25 May 2021 10:57:27 GMT</pubDate></item><item><title>CodePipeline for a Micronaut application Docker image</title><link>https://sergiodelamo.com/blog/micronaut-gradle-aws-codepipeline-codebuild-ecr-elasticbeanstalk-docker.html</link><description><![CDATA[<p>Continuous deployment - CodePipeline: Github → CodeBuild → ECR → Elastic Beanstalk</p><p><img src="https://images.sergiodelamo.com/codepipeline-github-codebuild-ecr-elasticbeanstalk.svg" alt="" /></p><p>Micronaut® framework is build agnostic, you can use either Gradle or Maven. However, Gradle is my build tool of choice.</p><h2>Changes to Gradle Build</h2><h3>Generate Dockerrun.aws.json</h3><p>Create a template for <code>Dockerrun.aws.json</code></p><blockquote><p>A <code>Dockerrun.aws.json</code> file describes how to deploy a remote Docker image as an Elastic Beanstalk application. This JSON file is specific to Elastic Beanstalk. If your application runs on an image that is available in a hosted repository, you can specify the image in a Dockerrun.</p></blockquote><p><em>Dockerrun.aws.json</em></p><pre><code class="language-json">{    &quot;AWSEBDockerrunVersion&quot;: &quot;1&quot;,    &quot;Image&quot;: {        &quot;Name&quot;: &quot;@awsAccountId@.dkr.ecr.@awsRegion@.amazonaws.com/@awsEcrRepositoryName@:@imageTag@&quot;,        &quot;Update&quot;: &quot;true&quot;    },    &quot;Ports&quot;: [        {            &quot;ContainerPort&quot;: &quot;5000&quot;        }    ]}    </code></pre><p>I define the properties in <code>gradle.properties</code></p><pre><code class="language-properties">projectVersion=0.0.5...awsAccountId=XXXXXXXawsRegion=us-east-1awsEcrRepositoryName=YYYYY</code></pre><p>Create a Gradle tasks to populate the JSON template:</p><p><em>build.gradle</em></p><pre><code class="language-groovy">import org.apache.tools.ant.filters.ReplaceTokenstask copyAndReplaceTokensFromDockerrun(type: Copy) {    from('Dockerrun.aws.json') {        include 'Dockerrun.aws.json'        filter(ReplaceTokens, tokens: [imageTag: projectVersion,                                       awsAccountId: awsAccountId,                                       awsRegion: awsRegion,                                       awsEcrRepositoryName: awsEcrRepositoryName])    }    into 'build/docker'}</code></pre><h3>Configure Container registry</h3><p>Configure the <a href="https://aws.amazon.com/ecr/">Amazon ECR</a> repository as the container registry:</p><p><em>build.gradle</em></p><pre><code class="language-groovy">version = projectVersion......dockerBuild {  images = [&quot;${awsAccountId}.dkr.ecr.${awsRegion}.amazonaws.com/${awsEcrRepositoryName}:$project.version&quot;]}</code></pre><h2>AWS CodeBuild</h2><p>When you create your CodeBuild project, in the environment section, check:</p><blockquote><p>[x] Enable this flag if you want to build Docker images or want your builds to get elevated privileges`</p></blockquote><h3>BuildSpec</h3><p>Create <a href="https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html"><code>buildspec.yml</code></a> file:</p><blockquote><p>A buildspec is a collection of build commands and related settings, in YAML format, that CodeBuild uses to run a build.</p></blockquote><pre><code class="language-yaml">version: 0.2phases:  install:    runtime-versions:      java: corretto11   pre_build:    commands:      - echo Logging in to Amazon ECR...      - $(aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION)  build:    commands:      - ./gradlew dockerPush      - ./gradlew copyAndReplaceTokensFromDockerrun  post_build:    finally:      - rm -f  /root/.gradle/caches/modules-2/modules-2.lock      - rm -fr /root/.gradle/caches/*/plugin-resolution/artifacts:  files:    - 'Dockerrun.aws.json'  base-directory: 'build/docker'cache:  paths:    - '/root/.gradle/caches/**/*'     - '/root/.gradle/wrapper/**/*'</code></pre><ul><li><code>dockerPush</code>, a task provided by the <a href="https://github.com/micronaut-projects/micronaut-gradle-plugin">Micronaut Gradle Plugin</a>, pushes a Docker Image built with the <a href="https://github.com/bmuschko/gradle-docker-plugin">Docker Gradle Plugin</a> to ECR, the configured container registry</li></ul><p>The build's artifact is the output of the Gradle <code>copyAndReplaceTokensFromDockerrun</code> task.</p><h3>AWS Code Build IAM Role</h3><p>Attach a policy to the IAM role used by <a href="https://aws.amazon.com/codebuild/">AWS CodeBuild</a> to allow the build to publishing a new image to a <a href="https://aws.amazon.com/ecr/">Amazon ECR</a> repository. For example, the AWS Managed policy <code>AmazonEC2ContainerRegistryPowerUser</code>.</p><h2>Elastic Beanstalk</h2><p>Create a <code>Web server environment</code>. As platform select <code>Docker running on a 64bit Amazon Linux 2</code>.</p><h3>AWS Elastic Beanstalk IAM Role</h3><p>Attach a policy to the EC2 IAM role used by <a href="https://aws.amazon.com/elasticbeanstalk/">AWS Elastic Beanstalk</a> to allow the environment to retrieve the image from <a href="https://aws.amazon.com/ecr/">Amazon ECR</a> repository. For example, the AWS Managed policy <code>AmazonEC2ContainerRegistryReadOnly</code>.</p><h3>Start Micronaut Application in port 5000</h3><p>Set environment variable <code>MICRONAUT_SERVER_PORT</code> with value <code>5000</code> in <a href="https://aws.amazon.com/elasticbeanstalk/">AWS Elastic Beanstalk</a> <code>Configuration → Software</code>.</p><h2>AWS CodePipeline</h2><p><a href="https://aws.amazon.com/codepipeline/">AWS CodePipeline</a>:</p><ul><li>Source: Github (Version 2)</li><li>Build Provider: AWS CodeBuild</li><li>Deploy Provider: AWS Elastic Beanstalk</li></ul>]]></description><guid>micronaut-gradle-aws-codepipeline-codebuild-ecr-elasticbeanstalk-docker</guid><pubDate>Tue, 25 May 2021 10:57:27 GMT</pubDate></item><item><title>AWS CodeBuild Buildspec.yml example for Gradle and a Micronaut Application</title><link>https://sergiodelamo.com/blog/aws-codebuild-buildspec-micronaut-gradle-app.html</link><description><![CDATA[<p>I use <a href="https://gradle.org">Gradle</a> as my build tool of choice for my <a href="https://micronaut.io">Micronaut</a> applications.</p><p>I often use <a href="https://aws.amazon.com/codebuild/">AWS CodeBuild</a> as my continuous integration service.</p><p>I use such a <a href="https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html"><code>buildspec.yml</code> file</a>:</p><blockquote><p>A buildspec is a collection of build commands and related settings, in YAML format, that CodeBuild uses to run a build.</p></blockquote><pre><code class="language-yaml">version: 0.2phases:  install:    runtime-versions:      java: corretto11   build:    commands:      - ./gradlew build --scan  post_build:    finally:      - rm -f  /root/.gradle/caches/modules-2/modules-2.lock      - rm -fr /root/.gradle/caches/*/plugin-resolution/artifacts:  files:    - '*-all.jar'  base-directory: 'build/libs'cache:  paths:    - '/root/.gradle/caches/**/*'     - '/root/.gradle/wrapper/**/*'</code></pre><p>My builds include the <a href="https://scans.gradle.com">Gradle Scan plugin</a>.</p><p>Because of that I can include the argument <code>--scan</code> which publishes a build scan. An available build scan makes easy to investigate build failures (access tests reports etc).</p>]]></description><guid>aws-codebuild-buildspec-micronaut-gradle-app</guid><pubDate>Mon, 24 May 2021 17:05:34 GMT</pubDate></item><item><title>cURL command to set a Telegram chatbot's webhook</title><link>https://sergiodelamo.com/blog/change-telegrambot-webhook.html</link><description><![CDATA[<p>To receive Telegram callbacks you have to set the webhook.</p><pre><code>curl -X &quot;POST&quot; &quot;https://api.telegram.org/xxx/setWebhook&quot;     -d '{&quot;url&quot;: &quot;https://yourapp.com/telegram/xxx&quot;}'      -H 'Content-Type: application/json; charset=utf-8'</code></pre><p>Replace <code>xxx</code> with the token which the <a href="https://t.me/botfather">Telegram Bot Father</a> provided you.</p><p>Telegram recommends you to include your token as a path variable in the webhook's url.</p><blockquote><p>If you'd like to make sure that the Webhook request comes from Telegram, we recommend using a secret path in the URL, e.g. https://www.example.com/<token>. Since nobody else knows your bot's token, you can be pretty sure it's us.</p></blockquote>]]></description><guid>change-telegrambot-webhook</guid><pubDate>Sat, 22 May 2021 06:44:34 GMT</pubDate></item><item><title>Test Micronaut POJOs contain validation annotations</title><link>https://sergiodelamo.com/blog/micronaut-test-validation.html</link><description><![CDATA[<p>The following POJO's fields are annotated with <code>javax.validation.constraints.NotNull</code> to indicate they are required.</p><pre><code class="language-java">import io.micronaut.core.annotation.Introspected;import io.micronaut.core.annotation.NonNull;import javax.validation.constraints.NotNull;import java.math.BigDecimal;import java.time.LocalDate;@Introspectedpublic class EuroExchangeResponse {    @NonNull    @NotNull    private BigDecimal rate;    @NonNull    @NotNull    private Currency target;    @NonNull    @NotNull    private LocalDate date;    public EuroExchangeResponse(@NonNull BigDecimal rate,                                @NonNull Currency target,                                @NonNull LocalDate date) {        this.rate = rate;        this.target = target;        this.date = date;    }    @NonNull    public BigDecimal getRate() {        return rate;    }    public void setRate(@NonNull BigDecimal rate) {        this.rate = rate;    }    @NonNull    public Currency getTarget() {        return target;    }    public void setTarget(@NonNull Currency target) {        this.target = target;    }    @NonNull    public LocalDate getDate() {        return date;    }    public void setDate(@NonNull LocalDate date) {        this.date = date;    }}</code></pre><p>I typically write such a test to verify that I did not forget to add the validation annotations.</p><pre><code class="language-groovy">import io.micronaut.context.ApplicationContextimport io.micronaut.core.beans.BeanIntrospectionimport spock.lang.AutoCleanupimport spock.lang.Sharedimport spock.lang.Specificationimport javax.validation.ConstraintViolationimport javax.validation.Validatorimport java.time.LocalDateclass EuroExchangeResponseSpec extends Specification {    @AutoCleanup    @Shared    ApplicationContext applicationContext = ApplicationContext.run()    @Shared    Validator validator = applicationContext.getBean(Validator)    void &quot;No constraint violations are found for valid model&quot;() {        given:        EuroExchangeResponse obj = valid()        when:        Set&lt;ConstraintViolation&lt;EuroExchangeResponse&gt;&gt; violations = validator.validate(obj)        then:        violations.isEmpty()    }    void &quot;rate cannot be null&quot;() {        given:        EuroExchangeResponse obj = valid()        when:        obj.rate = null        Set&lt;ConstraintViolation&lt;EuroExchangeResponse&gt;&gt; violations = validator.validate(obj)        then:        !violations.isEmpty()    }    void &quot;date cannot be null&quot;() {        given:        EuroExchangeResponse obj = valid()        when:        obj.date = null        Set&lt;ConstraintViolation&lt;EuroExchangeResponse&gt;&gt; violations = validator.validate(obj)        then:        !violations.isEmpty()    }    void &quot;target cannot be null&quot;() {        given:        EuroExchangeResponse obj = valid()        when:        obj.target = null        Set&lt;ConstraintViolation&lt;EuroExchangeResponse&gt;&gt; violations = validator.validate(obj)        then:        !violations.isEmpty()    }    static EuroExchangeResponse valid() {        new EuroExchangeResponse(0.861500, Currency.GBP, LocalDate.of(2021,05,17))    }}</code></pre>]]></description><guid>micronaut-test-validation</guid><pubDate>Mon, 17 May 2021 15:09:09 GMT</pubDate></item><item><title>Test Micronaut POJOs are annotated with @Introspected</title><link>https://sergiodelamo.com/blog/micronaut-test-pojo-annotated-with-at-introspected.html</link><description><![CDATA[<p>I always annotate my model classes with <code>@Introspected</code>. It is easy to test you don't forget the annotation.</p><p>Given a <a href="https://en.wikipedia.org/wiki/Plain_old_Java_object">POJO</a>:</p><pre><code class="language-java">import io.micronaut.core.annotation.Introspected;import io.micronaut.core.annotation.NonNull;import javax.validation.constraints.NotNull;import java.math.BigDecimal;import java.time.LocalDate;@Introspectedpublic class EuroExchangeResponse {    @NonNull    @NotNull    private BigDecimal rate;    @NonNull    @NotNull    private Currency target;    @NonNull    @NotNull    private LocalDate date;    public EuroExchangeResponse(@NonNull BigDecimal rate,                                @NonNull Currency target,                                @NonNull LocalDate date) {        this.rate = rate;        this.target = target;        this.date = date;    }    // Getters &amp; Setters}</code></pre><p>I write a <a href="https://blog.mrhaki.com/2011/01/spocklight-check-for-exceptions-with.html">Spock specification to verify that no exception is thrown</a> when I attempt to retrieve the <a href="https://docs.micronaut.io/latest/guide/#introspection">Bean Introspection</a>:</p><pre><code class="language-groovy">import io.micronaut.core.beans.BeanIntrospectionimport spock.lang.Specificationclass EuroExchangeResponseSpec extends Specification {    void &quot;EuroExchangeResponse is annotated with @Introspected&quot;() {        when:        BeanIntrospection.getIntrospection(EuroExchangeResponse)        then:        noExceptionThrown()    }}</code></pre>]]></description><guid>micronaut-test-pojo-annotated-with-at-introspected</guid><pubDate>Mon, 17 May 2021 14:48:21 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 010</title><link>https://sergiodelamo.com/blog/codigobot010.html</link><description><![CDATA[<p>Hablo con <a href="https://kinisoftware.com/">Kini</a> sobre los cambios en Basecamp.</p><p>Hemos recopilado enlaces sobre la controversia en las notas del episodio.</p><p>Hemos dividido en dos partes. <a href="https://codigobot.com/010.html">Ir a la Página del Podcast</a></p><p><a href="https://codigobot.com/010.html">Go to the linked site</a></p>]]></description><guid>codigobot010</guid><pubDate>Fri, 14 May 2021 09:12:42 GMT</pubDate></item><item><title>Testing JSON serialization with Micronaut applications</title><link>https://sergiodelamo.com/blog/micronaut-test-how-your-objects-are-serialized-into-json.html</link><description><![CDATA[<p>The following enum uses <a href="https://github.com/FasterXML/jackson">Jackson</a>'s <code>@JsonValue</code> annotation to render as lowercase string when serialized as JSON.</p><pre><code class="language-java">package example.micronaut;import com.fasterxml.jackson.annotation.JsonValue;import java.util.Locale;public enum NoteKind {	TRIX;	@JsonValue	@Override	public String toString() {		return this.name().toLowerCase(Locale.ENGLISH);	}}</code></pre><p>We can test it with <a href="https://spockframework.org">Spock</a> by injecting the <code>ObjectMapper</code> into your test:</p><pre><code class="language-groovy">package example.micronautimport com.fasterxml.jackson.databind.ObjectMapperimport io.micronaut.test.extensions.spock.annotation.MicronautTestimport spock.lang.Specificationimport javax.inject.Inject@MicronautTest(startApplication = false)class NoteKindSpec extends Specification {	@Inject	ObjectMapper objectMapper	void &quot;NoteKind is render as lowercase when serialized as JSON&quot;() {		given:		MockPojo pojo = new MockPojo(kind: NoteKind.TRIX)				expect:		objectMapper.writeValueAsString(pojo) == '{&quot;kind&quot;:&quot;trix&quot;}'	}	static class MockPojo {		NoteKind kind	}}</code></pre>]]></description><guid>micronaut-test-how-your-objects-are-serialized-into-json</guid><pubDate>Fri, 07 May 2021 15:44:51 GMT</pubDate></item><item><title>Make your podcast RSS feed publicly available</title><link>https://sergiodelamo.com/blog/make-my-feed-publicly-available.html</link><description><![CDATA[<p>If you host a podcast, you probably have an <a href="https://podcastsconnect.apple.com">Apple Podcast Connect</a> account.</p><p>If you do, please make sure you RSS feed is publicly available.</p><p><img src="https://images.sergiodelamo.com/public-rss-feed.png" alt="" /><img src="https://images.sergiodelamo.com/public-rss-feed.png" alt="" /></p>]]></description><guid>make-my-feed-publicly-available</guid><pubDate>Thu, 06 May 2021 16:32:10 GMT</pubDate></item><item><title>Test Micronaut Health endpoint</title><link>https://sergiodelamo.com/blog/micronaut-test-health-endpoint.html</link><description><![CDATA[<p><a href="https://docs.micronaut.io/latest/guide/#management">Micronaut management</a> dependency adds support for monitoring of your application via endpoints. For example, the <a href="https://docs.micronaut.io/latest/guide/#healthEndpoint">health endpoint</a> exposes the state of your application.</p><p>I test it with <a href="https://spockframework.org">Spock</a>:</p><pre><code class="language-groovy">package example.micronautimport io.micronaut.http.HttpRequestimport io.micronaut.http.client.HttpClientimport io.micronaut.http.client.annotation.Clientimport io.micronaut.test.extensions.spock.annotation.MicronautTestimport javax.inject.Injectimport spock.lang.Specificationimport io.micronaut.http.HttpResponseimport io.micronaut.http.HttpStatusimport io.micronaut.http.client.BlockingHttpClient@MicronautTestclass HealthSpec extends Specification {    @Inject    @Client(&quot;/&quot;)    HttpClient httpClient        void &quot;/health responds OK&quot;() {        given:        BlockingHttpClient client = httpClient.toBlocking()        HttpRequest&lt;?&gt; request = HttpRequest.GET('/health')                when:        HttpResponse&lt;Map&gt; response = client.exchange(request, Map)        then:        noExceptionThrown()        response.status() == HttpStatus.OK        response.body() == [status: 'UP']    }}</code></pre>]]></description><guid>micronaut-test-health-endpoint</guid><pubDate>Thu, 06 May 2021 06:05:17 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 009</title><link>https://sergiodelamo.com/blog/codigobot009.html</link><description><![CDATA[<p>Hablo con <a href="https://kinisoftware.com/">Kini</a> sobre su experiencia en Codemotion 2021 spanish edition, la resaca de aparecer en la newsletter de Alexa ...</p><p><a href="https://codigobot.com/009.html">Ir a la Página del Podcast</a></p><p><a href="https://codigobot.com/009.html">Go to the linked site</a></p>]]></description><guid>codigobot009</guid><pubDate>Fri, 30 Apr 2021 09:12:42 GMT</pubDate></item><item><title>Gradle, Jacoco and 90% Code Coverage</title><link>https://sergiodelamo.com/blog/micronaut-gradle-jacoco.html</link><description><![CDATA[<p>With <a href="https://docs.gradle.org/current/userguide/jacoco_plugin.html">Jacoco Gradle Plugin</a>it is easy to setup your project to enforce a coverage percentage.</p><p>I use the following configuration to enforce 90% both at class and at project level.</p><pre><code class="language-groovy">plugins {    ...	id('jacoco')}......check {	dependsOn jacocoTestCoverageVerification}jacocoTestReport {	dependsOn test}jacocoTestReport {	reports {		xml.enabled false		csv.enabled false	}}jacocoTestCoverageVerification {    violationRules {	    rule {		    limit {			    minimum = 0.9		    }	    }	    rule {		    element = 'CLASS'		    excludes = ['example.micronaut.Application']		    limit {			    minimum = 0.9		    }	    }    } }</code></pre><p>The above example excludes the <code>Application</code> class.</p><p>Yes, I use Apache Groovy Gradle DSL.</p>]]></description><guid>micronaut-gradle-jacoco</guid><pubDate>Thu, 29 Apr 2021 20:24:32 GMT</pubDate></item><item><title>SpringBoot to the Micronaut® framework - @ResponseStatus Exception</title><link>https://sergiodelamo.com/blog/springboot-micronaut-statusresponseexception.html</link><description><![CDATA[<p>In SpringBoot you can annotate your exceptions with <code>@ResponseStatus</code>, Micronaut's <code>HttpStatusException</code> achieves a similar behaviour</p><h2><a href="https://spring.io/projects/spring-boot">SpringBoot</a></h2><p>In SpringBoot you can annotate an exception with <code>@ResponseStatus</code></p><pre><code class="language-java">package scorekeep;import org.springframework.web.bind.annotation.ResponseStatus;import org.springframework.http.HttpStatus;@ResponseStatus(value=HttpStatus.UNPROCESSABLE_ENTITY, reason=&quot;Game does not exist.&quot;)public class GameNotFoundException extends RuntimeException {}</code></pre><p>If your logic raises such a exception:</p><pre><code class="language-java">package scorekeep;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;import java.util.Collections;import java.util.Map;@RestController@RequestMapping(&quot;/api/score&quot;)public class ScoreController {	@RequestMapping(value = &quot;/{gameId}&quot;, method = RequestMethod.GET)	public Map&lt;String, Object&gt; index(@PathVariable String gameId) {		if (!gameId.equals(&quot;AAA&quot;)) {			throw new GameNotFoundException();		}		return Collections.singletonMap(&quot;messages&quot;, &quot;Hello World&quot;);	}}</code></pre><p>You get:</p><pre><code class="language-bash">% curl -i localhost:8080/api/score/ZZZHTTP/1.1 422 Content-Type: application/jsonTransfer-Encoding: chunkedDate: Tue, 27 Apr 2021 06:09:45 GMT{&quot;timestamp&quot;:&quot;2021-04-27T06:09:45.703+00:00&quot;,&quot;status&quot;:422,&quot;error&quot;:&quot;Unprocessable Entity&quot;,&quot;message&quot;:&quot;&quot;,&quot;path&quot;:&quot;/api/score/ZZZ&quot;}</code></pre><h2><a href="https:/micronaut.io">Micronaut Framework</a></h2><p>A similar error processing in Micronaut® framework could be achieve by extending <code>HttpStatusException</code></p><pre><code class="language-java">package scorekeep;import io.micronaut.http.HttpStatus;import io.micronaut.http.exceptions.HttpStatusException;public class GameNotFoundException extends HttpStatusException {	public GameNotFoundException() {		super(HttpStatus.UNPROCESSABLE_ENTITY, &quot;Game does not exist.&quot;);	}}</code></pre><p>If your logic raises such a exception:</p><pre><code class="language-java">package scorekeep;import io.micronaut.http.annotation.Controller;import io.micronaut.http.annotation.Get;import io.micronaut.http.annotation.PathVariable;import java.util.Collections;import java.util.Map;@Controller(&quot;/api/score&quot;)public class ScoreController {	@Get( &quot;/{gameId}&quot;)	public Map&lt;String, Object&gt; index(@PathVariable String gameId) {		if (!gameId.equals(&quot;AAA&quot;)) {			throw new GameNotFoundException();		}		return Collections.singletonMap(&quot;messages&quot;, &quot;Hello World&quot;);	}}</code></pre><p>You get:</p><pre><code class="language-bash">curl -i localhost:8080/api/score/ZZZHTTP/1.1 422 Unprocessable EntityContent-Type: application/jsoncontent-length: 96connection: keep-alive{&quot;message&quot;:&quot;Game does not exist.&quot;,&quot;_links&quot;:{&quot;self&quot;:{&quot;href&quot;:&quot;/api/score/ZZZ&quot;,&quot;templated&quot;:false}}}%  </code></pre><h3>Error Formatting</h3><p>By default, Micronaut® framework error formatting uses <a href="https://github.com/blongden/vnd.error">vnd.error</a>. I prefer to use <a href="https://tools.ietf.org/html/rfc7807">Problem+JSON</a>. To use Problem+JSON in Micronaut® framework, include <a href="https://micronaut-projects.github.io/micronaut-problem-json/snapshot/guide/">micronaut-problem-json</a> dependency and you will get:</p><pre><code class="language-bash">curl -i localhost:8080/api/score/ZZZHTTP/1.1 422 Unprocessable EntityContent-Type: application/problem+jsoncontent-length: 67connection: keep-alive{&quot;type&quot;:&quot;about:blank&quot;,&quot;status&quot;:422,&quot;detail&quot;:&quot;Game does not exist.&quot;}</code></pre><p>Moreover, <a href="https://docs.micronaut.io/latest/guide/#exceptionHandler">Micronaut Error Handling</a> is easy to customise to suit your needs.</p>]]></description><guid>springboot-micronaut-statusresponseexception</guid><pubDate>Wed, 28 Apr 2021 07:05:01 GMT</pubDate></item><item><title>SpringBoot to the Micronaut® framework - Controller annotations</title><link>https://sergiodelamo.com/blog/springboot-to-micronaut-requestmapping.html</link><description><![CDATA[<p>SpringBoot and Micronaut applications ease the creation of routes with similar annotations.</p><p><a href="https://spring.io/projects/spring-boot">SpringBoot</a>:</p><pre><code class="language-java">package scorekeep;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;import java.util.Collections;import java.util.Map;@RestController@RequestMapping(&quot;/api/score&quot;)public class ScoreController {    @RequestMapping(value = &quot;/{gameId}&quot;, method = RequestMethod.GET)    public Map&lt;String, Object&gt; index(@PathVariable String gameId) {                return Collections.singletonMap(&quot;messages&quot;, &quot;Hello World&quot;);    }}</code></pre><p><a href="https:/micronaut.io">Micronaut® framework</a>:</p><pre><code class="language-java">package scorekeep;import io.micronaut.http.annotation.Controller;import io.micronaut.http.annotation.Get;import io.micronaut.http.annotation.PathVariable;import java.util.Collections;import java.util.Map;@Controller(&quot;/api/score&quot;)public class ScoreController {    @Get( &quot;/{gameId}&quot;)    public Map&lt;String, Object&gt; index(@PathVariable String gameId) {        return Collections.singletonMap(&quot;messages&quot;, &quot;Hello World&quot;);    }}</code></pre><p>I like to include the <code>@PathVariable</code> annotation to convey the parameter context. However, it is not required.</p><p>It is easy to migrate Spring Boot request mapping annotations to Micronaut annotations. Micronaut® framework includes annotations for the HTTP verbs <code>@Get</code>, <code>@Post</code>, <code>@Delete</code>, <code>@Put</code>... They are succinct. Succinct code is good code.</p>]]></description><guid>springboot-to-micronaut-requestmapping</guid><pubDate>Tue, 27 Apr 2021 09:45:56 GMT</pubDate></item><item><title>SpringBoot to Micronaut® framework - Application Class</title><link>https://sergiodelamo.com/blog/springboot-to-micronaut-application.html</link><description><![CDATA[<p>SpringBoot and Micronaut applications contain a simple application class which starts the application for you.</p><p><a href="https://spring.io/projects/spring-boot">SpringBoot</a>:</p><pre><code class="language-java">package scorekeep;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class Application {	public static void main(String[] args) {		SpringApplication.run(Application.class, args);	}}</code></pre><p><a href="https:/micronaut.io">Micronaut® framework</a>:</p><pre><code class="language-java">package scorekeep;import io.micronaut.runtime.Micronaut;public class Application {	public static void main(String[] args) {		Micronaut.run(Application.class, args);	}}</code></pre><p>Except for the <code>@SpringBootApplication</code> annotation both classes are almost identical.</p>]]></description><guid>springboot-to-micronaut-application</guid><pubDate>Tue, 27 Apr 2021 05:45:56 GMT</pubDate></item><item><title>Host and IP Resolution in a Micronaut application with Load Balancer and Elastic Beanstalk</title><link>https://sergiodelamo.com/blog/host-and-ip-resolution-micronaut-load-balancer-elastic-beanstalk.html</link><description><![CDATA[<p>When you run your application behind a <a href="https://aws.amazon.com/elasticloadbalancing">Elastic Load Balancer</a>, a <a href="https://aws.amazon.com/elasticloadbalancing/classic-load-balancer/">Classic Load Balancer</a> or an <a href="https://aws.amazon.com/elasticloadbalancing/application-load-balancer/">Application Load Balancer</a>, AWS decorates your request with <a href="https://en.wikipedia.org/wiki/X-Forwarded-For">X-Forward HTTP Headers</a>.</p><p><img src="https://images.sergiodelamo.com/elb-elasticbeanstalk-x-forwarded-headers.png" alt="" /></p><p>In a Micronaut application, you may need to resolve the domain name of your application, for example when using OAuth 2.0 for a <code>redirect_uri</code>, or restrict an endpoint to a range of IP Addresses, for example a Stripe's callback.</p><p>Micronaut® framework contains two APIs:</p><ul><li><code>HttpHostResolver</code> to resolve the host</li><li><code>HttpClientAddressResolver</code> to resolve the IP.</li></ul><p>Given the following controller:</p><p><em><code>src/main/java/example/micronaut/HeadersController.java</code></em></p><pre><code class="language-java">package example.micronaut;import io.micronaut.http.HttpRequest;import io.micronaut.http.annotation.Controller;import io.micronaut.http.annotation.Get;import io.micronaut.http.server.util.HttpClientAddressResolver;import io.micronaut.http.server.util.HttpHostResolver;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.util.HashMap;import java.util.Map;@Controllerpublic class HeadersController {	private static final Logger LOG = LoggerFactory.getLogger(HeadersController.class);	private final HttpHostResolver httpHostResolver;	private final HttpClientAddressResolver httpClientAddressResolver;	public HeadersController(HttpHostResolver httpHostResolver,							 HttpClientAddressResolver httpClientAddressResolver) {		this.httpHostResolver = httpHostResolver;		this.httpClientAddressResolver = httpClientAddressResolver;	}	@Get	public Map&lt;String, Object&gt; index(HttpRequest&lt;?&gt; request) {		for (String name : request.getHeaders().names()) {			if (LOG.isInfoEnabled()) {				LOG.info(&quot;H {}: {}&quot;, name, request.getHeaders().get(name));			}		}		Map&lt;String, Object&gt; result = new HashMap&lt;&gt;();		result.put(&quot;ip&quot;, httpClientAddressResolver.resolve(request));		result.put(&quot;resolved_host&quot;, httpHostResolver.resolve(request));		return result;	}}</code></pre><p>If I run this application locally, I get:</p><pre><code class="language-bash">% curl localhost:8080{&quot;ip&quot;:&quot;0:0:0:0:0:0:0:1&quot;,&quot;resolved_host&quot;:&quot;http://localhost:8080&quot;}</code></pre><p>with logs:</p><pre><code class="language-bash">H Host: localhost:8080H User-Agent: curl/7.64.1H Accept: */*</code></pre><p>To resolve the Host and IP in <a href="https://aws.amazon.com/elasticbeanstalk/">Elastic Beanstalk</a> behind an Application Load Balancer add:</p><p><em><code>src/main/resources/application-ec2.yml</code></em></p><pre><code class="language-yaml">micronaut:  server:	host-resolution:	  protocol-header: X-Forwarded-Proto	  port-header: X-Forwarded-Port	  host-header: Host	client-address-header: X-Real-IP</code></pre><p>EC2 environment is automatically detected, because of that I typically use <code>application-ec2.yml</code> for such configuration.</p><p>If you call your application's endpoint running in Elastic Beanstalk you get:</p><pre><code class="language-bash">% curl http://micronaut.example{&quot;ip&quot;:&quot;79.150.119.41&quot;,&quot;resolved_host&quot;:&quot;http://micronaut.example&quot;}</code></pre><p>with logs:</p><pre><code class="language-bash">H Connection: upgradeH Host: micronaut.exampleH X-Real-IP: 172.31.91.156H X-Forwarded-For: 79.150.119.41, 172.31.91.156H X-Forwarded-Proto: httpH X-Forwarded-Port: 80H X-Amzn-Trace-Id: Root=1-60865015-39f63e107ca0f25410b0e66eH User-Agent: curl/7.64.1H Accept: */*</code></pre>]]></description><guid>host-and-ip-resolution-micronaut-load-balancer-elastic-beanstalk</guid><pubDate>Sun, 25 Apr 2021 09:28:04 GMT</pubDate></item><item><title>KSUID (K-Sortable Unique IDentifier)</title><link>https://sergiodelamo.com/blog/ksuid.html</link><description><![CDATA[<p><a href="https://github.com/segmentio/ksuid">KSUID</a>:</p><blockquote><p>KSUID is for K-Sortable Unique IDentifier. It is a kind of globally unique identifier similar to a RFC 4122 UUID, built from the ground-up to be &quot;naturally&quot; sorted by generation timestamp without any special type-aware logic.</p></blockquote><p>It was originally open-sourced as a Go Library by <a href="https://segment.com">Segment</a>.</p><p>A sorteable unique identifier is useful in scenarios where you need your primary keys to be sorteable. For example, if you need to generate keys for your items in <a href="https://aws.amazon.com/dynamodb/">DynamoDB</a>. In fact, I learned about KSUID while reading <a href="https://www.dynamodbbook.com">DynamoDB Book</a></p><p>There is a <a href="https://github.com/akhawaja/ksuid">Java implementation of Segment's KSUID library</a>. You can generate a KSUID String with: <code>new Ksuid().generate()</code>.</p><p>For example, if you have a <code>Contact</code> POJO:</p><pre><code class="language-java">package example;import com.amirkhawaja.Ksuid;import java.io.IOException;public class Contact implements Comparable&lt;Contact&gt; {    private final String id;    private final String name;    private final String title;    public Contact(String name, String title) throws IOException {        this.id = new Ksuid().generate();        this.name = name;        this.title = title;    }    @Override    public int compareTo(Contact o) {        return o.getId().compareTo(getId());    }    public String getName() {        return name;    }    public String getTitle() {        return title;    }    public String getId() {        return id;    }    @Override    public String toString() {        return &quot;Contact{&quot; +                &quot;id='&quot; +  id + &quot; &quot; + new Ksuid().parse(id) + '\'' +                &quot;, name='&quot; + name + '\'' +                &quot;, title='&quot; + title + '\'' +                '}';    }    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;        Contact contact = (Contact) o;        if (id != null ? !id.equals(contact.id) : contact.id != null) return false;        if (name != null ? !name.equals(contact.name) : contact.name != null) return false;        return title != null ? title.equals(contact.title) : contact.title == null;    }    @Override    public int hashCode() {        int result = id != null ? id.hashCode() : 0;        result = 31 * result + (name != null ? name.hashCode() : 0);        result = 31 * result + (title != null ? title.hashCode() : 0);        return result;    }}</code></pre><p>You can sort it via the primary key:</p><pre><code class="language-java">Contact tim = new Contact(&quot;Tim Cook&quot;, &quot;CEO&quot;);Contact katherine = new Contact(&quot;Katherine Adams&quot;, &quot;Senior Vice President and General Counsel&quot;);Contact eddy = new Contact(&quot;Eddy Cue&quot;, &quot;Senior Vice President Internet Software and Services&quot;);Contact craig = new Contact(&quot;Craig Federighi&quot;, &quot;Senior Vice President Software Engineering&quot;);List&lt;Contact&gt; contacts = new ArrayList&lt;&gt;();contacts.add(craig);contacts.add(eddy);contacts.add(tim);contacts.add(katherine);            Collections.sort(contacts);assert contacts.get(0).equals(tim);System.out.println(tim.toString());</code></pre><p>The primary key contains the timestamp. The previous code prints:</p><pre><code class="language-java">Contact{id='DRIvC0YHDXBuGawemfYsenzBN84 Time: 2021-04-24T20:18:19Z[UTC]Timestamp: 1619295499Payload: [70, 7, 13, 112, 110, 25, -84, 30, -103, -10, 44, 122, 124, -63, 55, -50]', name='Tim Cook', title='CEO'}</code></pre>]]></description><guid>ksuid</guid><pubDate>Sat, 24 Apr 2021 20:39:04 GMT</pubDate></item><item><title>&#x1f468;&#x1f3fb;‍&#x1f3eb; Integrating Micronaut apps with AWS</title><link>https://sergiodelamo.com/blog/micronaut-aws-2021-course.html</link><description><![CDATA[<p>I will be teaching a Micronaut / Amazon Web Services workshop at the end of May.</p><p>🔗 <a href="https://objectcomputing.com/services/training/catalog/micronaut-training/micronaut-aws-integration">Enrollment webpage</a></p><p>📅 Dates: May 25 to May 28</p><p>⏳ Time: 9:00 a.m. to 12:00 p.m. CDT</p><p>⏱ Duration: 12 hours (3 hours per day for 4 days)</p><p>📍Location: Online</p><p>👨🏻‍🏫 Instructor: Yours truly</p><p>💵 Registration Fee: $199.00 USD</p><h2>Outline</h2><blockquote><p>The following topics are covered in this workshop:</p></blockquote><blockquote><ul><li>Deploying a Micronaut app to Elastic Beanstalk</li><li>Accessing a relational database hosted with RDS with Micronaut Data</li><li>NoSQL persistence with DynamoDB and the Micronaut® framework</li><li>Uploading files to S3 with the Micronaut® framework</li><li>Transactional emails with SES and the Micronaut® framework</li><li>Build a CI/CD pipeline for your Micronaut apps with CodeBuild and CodePipeline</li><li>Handle your Micronaut app's user management and authentication with Cognito</li><li>Serverless functions with the Micronaut® framework and Lambda</li><li>Build Alexa skills with the Micronaut® framework</li><li>Monitoring with Cloud Watch</li><li>Tracing with X-Ray</li><li>Deploying Micronaut GraalVM Native Images to AWS</li></ul></blockquote><p><a href="https://objectcomputing.com/services/training/catalog/micronaut-training/micronaut-aws-integration">Register</a></p><p><a href="https://objectcomputing.com/services/training/catalog/micronaut-training/micronaut-aws-integration">Go to the linked site</a></p>]]></description><guid>micronaut-aws-2021-course</guid><pubDate>Sat, 24 Apr 2021 05:39:23 GMT</pubDate></item><item><title>NetNewsWire introduces the ability to subscribe to Twitter searches of mentions</title><link>https://sergiodelamo.com/blog/netnewswire-twitter-feeds.html</link><description><![CDATA[<p>You can now subscribe to Twitter searches or mentions as a feed directly in NetNewsWire.</p><p><img src="https://images.sergiodelamo.com/netnewswire-twitter-feed-1.jpg" alt="NetNewsWire Twitter Extension" /></p><p><img src="https://images.sergiodelamo.com/netnewswire-twitter-feed-2.jpg" alt="NetNewsWire Twitter Micronaut hashtag subscription" /></p><p><a href="https://netnewswire.com">NetNewsWire</a> is such a great app for Mac and iOS. It is <a href="https://craigmod.com/essays/fast_software/">Fast Software, the Best Sofware</a>.</p><p><a href="https://netnewswire.com"><img src="https://images.sergiodelamo.com/netnewswire-twitter-feed-2.jpg" alt="" /></a></p><p><a href="https://netnewswire.com">Go to the linked site</a></p>]]></description><guid>netnewswire-twitter-feeds</guid><pubDate>Fri, 23 Apr 2021 07:52:05 GMT</pubDate></item><item><title>Micronaut and Camunda</title><link>https://sergiodelamo.com/blog/micronaut-and-camunda-video.html</link><description><![CDATA[<p>Webinar recording about integrating <a href="https://micronaut.io">Micronaut</a> and <a href="https://camunda.com">Camunda</a>. I joined <a href="https://twitter.com/toschaef">Tobias Schäfer</a> and <a href="https://twitter.com/berndruecker">Bernd Rücker</a>.</p><p>Checkout the <a href="https://github.com/NovatecConsulting/micronaut-camunda-bpm">Micronaut + Camunda integration in Github</a></p><p><a href="https://www.youtube.com/watch?v=sg1y1J7ao3E">Go to the linked site</a></p>]]></description><guid>micronaut-and-camunda-video</guid><pubDate>Thu, 22 Apr 2021 16:42:25 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 008</title><link>https://sergiodelamo.com/blog/codigobot-008.html</link><description><![CDATA[<p>Hablo con <a href="https://kinisoftware.com/">Kini</a> sobre como compartir conocimiento dentro de la empresa. Organizar charlas, alojar eventos, conferencias internas...</p><p><a href="https://codigobot.com/008.html">Ir a la Página del Podcast</a></p><p><a href="https://codigobot.com/008.html">Go to the linked site</a></p>]]></description><guid>codigobot-008</guid><pubDate>Fri, 16 Apr 2021 09:12:42 GMT</pubDate></item><item><title>Launch Fantastical with a x-callback-url</title><link>https://sergiodelamo.com/blog/open-fantastical-with-a-x-callback-url-link.html</link><description><![CDATA[<p>I have a <em>Morning Routing</em> project in <a href="https://www.omnigroup.com/omnifocus/">Omnifocus</a> which includes an entry to check <a href="https://flexibits.com/fantastical">Fantastical</a>. In the Omnifocus item's note, I have a link to open Fantastical.</p><p><a href="x-fantastical3://show/calendar">x-fantastical3://show/calendar</a></p><p>This is possible thanks to <a href="http://x-callback-url.com">x-callback-url</a> links:</p><blockquote><p>Using x-callback-url’s source apps can launch other apps passing data and context information, and also provide parameters instructing the target app to return data and control back to the source app after executing an action.</p></blockquote>]]></description><guid>open-fantastical-with-a-x-callback-url-link</guid><pubDate>Mon, 05 Apr 2021 06:25:25 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 007</title><link>https://sergiodelamo.com/blog/codigobot-007.html</link><description><![CDATA[<p>Hablo con <a href="https://kinisoftware.com/">Kini</a> sobre servicios AWS. Los que usan para distribuir el Podcast y los que usan en otros proyectos.</p><p><a href="https://codigobot.com/007.html">Ir a la Página del Podcast</a></p><p><a href="https://codigobot.com/007.html">Go to the linked site</a></p>]]></description><guid>codigobot-007</guid><pubDate>Sun, 04 Apr 2021 09:06:36 GMT</pubDate></item><item><title>GDPR for Developers</title><link>https://sergiodelamo.com/blog/gdpr-notes.html</link><description><![CDATA[<p>keywords:gdpr</p><p>I have watched <a href="https://wordpress.tv/speakers/andrew-norcross/">Andrew Norcross's GDPR for Developers</a> talks.</p><p>I downloaded his slides and these are my notes:</p><p><strong>GDPR</strong> stands for <strong>G</strong>eneral <strong>D</strong>ata <strong>P</strong>rotection <strong>R</strong>egulation</p><p>Keys:</p><ul><li><strong>Right to be informed.</strong></li><li><strong>Right to data access.</strong></li><li><strong>Right to be forgotten.</strong></li></ul><h2>Are users worried about Privacy?</h2><ul><li>92% worry about their privacy online</li><li>31% understand how companies collect and share their personal information.</li><li>74% have limited their online activity in the last year due to privacy concerns.</li></ul><h2>Data</h2><p>This is all about data. In specific, personal user data.</p><p>In specific, data defined as <em>any information relating to an identified or identifiable natural person</em>.</p><p>Data is worth Money.</p><h3>Data Ownership</h3><p><strong>The data does not belong to you anymore</strong>.</p><blockquote><p>Because really, if you can't carry it on your back at a dead run, it never belong you anyway.</p></blockquote><p>Patrick Graney.<br />American Philosopher</p><p>We have never really considered the ownership of the data that came through the software we built.</p><h2>Why does this matter?</h2><p>James Liang. he’s the engineer for Volkswagen who was sentences to 40 months in prison for their emissions scandal.</p><p>An important note from the case: <em>While he was not the ‘mastermind’ he played a large role</em>.</p><h2>What should I do about it?</h2><h3>What defines Personal Data?</h3><p><em>Any information relating to an identified or identifiable natural person</em>.  which is different than <em>personally identifiable information</em>.</p><ul><li>Racial or ethnic origin</li><li>Political opinions</li><li>Religious or philosophical beliefs</li><li>Trade union membership</li><li>Health data</li><li>Sex life or sexual orientation</li><li>Past or spent criminal convictions</li><li>Genetic data</li><li>Biometric data</li><li>Location data</li><li>Pseudonymized data</li><li>Online identifiers</li></ul><h4>Online Identifiers</h4><ul><li>IP addresses</li><li>Mobile device ids</li><li>RFID tags</li><li>MAC Addresses</li><li>Cookies / telemetry data</li><li>User ids and emails</li></ul><h2>Does it apply to me?</h2><p><strong>GDPR applies to everyone</strong> (who does business with the EU).GDPR applies to all businesses, organizations, sectors, situations, and scenarios, regardless of size, employees, or revenues.</p><h2>How does it work?</h2><p>Everything involving the entire data collection process basically falls under two baskets:</p><ul><li><strong>Data Controllers</strong> - decides what data is collected, how it is used, and whom it is shared with.</li><li><strong>Data Processor</strong> - any entity other than the data controller who processes the data on their behalf.</li></ul><h1>Privacy by design</h1><p>Its a seven-point development methodology which requires optimal data protection to be provided as standard, by default, across all uses and applications.</p><h1>Need to know everything</h1><ul><li>What / how you’re collecting</li><li>Where / how you’re storing it</li><li>Who can access it</li></ul><h2>Privacy impact assessment</h2><p>This is a requirement for data-intensive projects and needs to be documented and made accessible for anyone involved with the project and can be requested by regulators in the event of a breach.</p><h2>FAQ</h2><ul><li>Is this actually enforced? Yes. In January of 2019, France fines Google $57 million for GDPR violations.</li><li>Will they really notice me? A German social media provider received an order to pay a €20,000 fine for a data breach that occurred in the summer of 2018. and they cooperated every step of the way.</li><li>Ignorance is not an excuse.</li><li></li></ul><h2>Conclusion</h2><ul><li>Privacy is paramount. User privacy (and security) is more important that anything marketing wants.</li></ul><p><a href="https://wordpress.tv/speakers/andrew-norcross/">Go to the linked site</a></p>]]></description><guid>gdpr-notes</guid><pubDate>Fri, 26 Mar 2021 06:29:57 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 006</title><link>https://sergiodelamo.com/blog/codigobot-006.html</link><description><![CDATA[<p>Hablo con <a href="https://kinisoftware.com/">Kini</a> sobre de creación de contenido, crear de tu marca personal y sobre probar nuevas plataformas. El miedo y envidias de los creadores de contenido.</p><p><a href="https://codigobot.com/006.html">Ir a la Página del Podcast</a></p><p><a href="https://codigobot.com/006.html">Go to the linked site</a></p>]]></description><guid>codigobot-006</guid><pubDate>Sat, 20 Mar 2021 09:06:36 GMT</pubDate></item><item><title>&#x1f5e3; Micronaut Security Course</title><link>https://sergiodelamo.com/blog/micronaut-security-course-2021.html</link><description><![CDATA[<p>I will be teaching a <a href="https://micronaut-projects.github.io/micronaut-security/latest/guide/index.html">Micronaut Security</a> 12 hour course next week. I am confident that after the course you will understand how to build secured Micronaut applications and how to test them. You don't need to be a Micronaut security expert. We will start from zero but we will go to more complex topics; JWT, JWKS or integration with OAuth 2.0 servers.</p><p>📅 Dates: March 16, 17, 18, and 19, 2021.<br />⏳ Time: 9:00 a.m. to 12:00 p.m. CDT.<br />📍 Location: Online.<br />👨🏻‍🏫 Instructor: Yours Truly.<br />💵 Registration Fee: $199.00 USD.</p><p><a href="https://objectcomputing.com/services/training/catalog/micronaut-training/micronaut-security-deep-dive">More Info</a></p><p><a href="https://objectcomputing.com/training/register/offering/289">Enroll</a></p><p><a href="https://objectcomputing.com/services/training/catalog/micronaut-training/micronaut-security-deep-dive">Go to the linked site</a></p>]]></description><guid>micronaut-security-course-2021</guid><pubDate>Fri, 12 Mar 2021 16:46:31 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 005</title><link>https://sergiodelamo.com/blog/codigobot-005.html</link><description><![CDATA[<p>Hablo con <a href="https://kinisoftware.com/">Kini</a> sobre conferencias. El futuro del circuito de conferencias. Conferencias online vs conferencias offline.</p><p><a href="https://codigobot.com/005.html">Ir a la Página del Podcast</a></p><p><a href="https://codigobot.com/005.html">Go to the linked site</a></p>]]></description><guid>codigobot-005</guid><pubDate>Sat, 06 Mar 2021 06:06:36 GMT</pubDate></item><item><title>&#x1f5e3; Automating Processes with Microservices on Micronaut® framework and Camunda</title><link>https://sergiodelamo.com/blog/micronaut-camunda.html</link><description><![CDATA[<p>I will be talking with <a href="https://twitter.com/toschaef">Tobias Schäfer</a> and <a href="https://twitter.com/berndruecker">Bernd Rücker</a> about integrating <a href="https://micronaut.io">Micronaut</a> and <a href="https://camunda.com">Camunda</a>.</p><blockquote><p>Workflow engines can complement modern microservice architectures to address common challenges.</p></blockquote><blockquote><p>In this complimentary webinar, we discuss these challenges and demonstrate how easy it is to add a workflow engine to your Micronaut environment. For this purpose, we provide an open source integration project to embed the Camunda workflow engine into Micronaut projects. The integration sets sensible defaults, so you can get started with minimum configuration: simply add a dependency in your Micronaut project.</p></blockquote><p>📅 2021-02-25 10:00</p><p>Online. It is free.</p><p><a href="https://us02web.zoom.us/webinar/register/WN_9Qo6JspDQmWKb2t6v1UtIA">Join me</a></p><p><img src="https://images.sergiodelamo.com/camundaandmicronaut.jpg" alt="" /></p><p><a href="https://us02web.zoom.us/webinar/register/WN_9Qo6JspDQmWKb2t6v1UtIA">Go to the linked site</a></p>]]></description><guid>micronaut-camunda</guid><pubDate>Fri, 19 Feb 2021 06:52:14 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 004</title><link>https://sergiodelamo.com/blog/codigobot-004.html</link><description><![CDATA[<p>Hablo con <a href="https://kinisoftware.com/">Kini</a> sobre la Telegram Chatbots.</p><p><a href="https://codigobot.com/004.html">Ir a la Página del Podcast</a></p><p><a href="https://codigobot.com/004.html">Go to the linked site</a></p>]]></description><guid>codigobot-004</guid><pubDate>Fri, 19 Feb 2021 06:20:25 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 003</title><link>https://sergiodelamo.com/blog/codigobot-003.html</link><description><![CDATA[<p>Hablo con <a href="https://kinisoftware.com/">Kini</a> sobre la <a href="https://twitter.com/ComunidadAlexa">Comunidad de Alexa en Español</a> y las personas involucradas en ella.</p><p><a href="https://codigobot.com/003.html">Ir a la Página del Podcast</a></p><p><a href="https://codigobot.com/003.html">Go to the linked site</a></p>]]></description><guid>codigobot-003</guid><pubDate>Sat, 06 Feb 2021 06:20:25 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 002</title><link>https://sergiodelamo.com/blog/codigobot-002.html</link><description><![CDATA[<blockquote><p><a href="https://kinisoftware.com">Kini</a> y  <a href="https://sergiodelamo.com">Sergio</a> hablan del <a href="https://www.amazon.es/Informativo-de-Ángel-Mart%C3%ADn-Oficial/dp/B08LHJYMY3/ref=cm_cr_arp_d_product_top?ie=UTF8">Alexa Skill - Informativo de Ángel Martín</a>. Tratan la historia del Skill y los servicios de AWS usados para desarrollarla</p></blockquote><p><a href="https://codigobot.com/002.html">Ir a la página del Podcast</a></p><p><a href="https://codigobot.com/002.html">Go to the linked site</a></p>]]></description><guid>codigobot-002</guid><pubDate>Fri, 22 Jan 2021 16:07:16 GMT</pubDate></item><item><title>&#x1f1ea;&#x1f1f8; - &#x1f399;Código Bot 001</title><link>https://sergiodelamo.com/blog/codigobot001.html</link><description><![CDATA[<blockquote><p><a href="https://kinisoftware.com">Kini</a> y  <a href="https://sergiodelamo.com">Sergio</a> hablan sobre objetivos anuales. Y para ello repasan los <a href="https://groovycalamari.com/issues/171/index.html#start">objetivos anuales que se fijo Sergio para 2020</a>.</p></blockquote><p><a href="https://codigobot.com/001.html">Ir a la Página del Podcast</a></p><p><a href="https://codigobot.com/001.html">Go to the linked site</a></p>]]></description><guid>codigobot001</guid><pubDate>Fri, 15 Jan 2021 15:20:24 GMT</pubDate></item><item><title>ApacheCon 2020: Taming your browser with Geb</title><link>https://sergiodelamo.com/blog/taming-your-browser-with-geb.html</link><description><![CDATA[<p>Video of my talk at ApacheCon 2020 about Geb</p><p><a href="https://www.youtube.com/watch?v=cU44CV0Sbtc">Go to the linked site</a></p>]]></description><guid>taming-your-browser-with-geb</guid><pubDate>Sat, 26 Dec 2020 07:41:55 GMT</pubDate></item><item><title>Micronaut Webinar: SPAs with Svelte and the Micronaut® framework</title><link>https://sergiodelamo.com/blog/spas-with-svelte-and-micronaut.html</link><description><![CDATA[<p>Video of the webinar about combining Svelte and the Micronaut® framework.</p><p>We discuss how to tackle common concerns (API security, documentation and versioning) when combining Micronaut with a Javascript front-end.</p><p><a href="https://www.youtube.com/watch?v=n_ylrgl84cQ">Register</a></p><p><a href="https://www.youtube.com/watch?v=n_ylrgl84cQ">Go to the linked site</a></p>]]></description><guid>spas-with-svelte-and-micronaut</guid><pubDate>Fri, 25 Dec 2020 07:52:19 GMT</pubDate></item><item><title>LondonGUG: What's new with Grails 4</title><link>https://sergiodelamo.com/blog/grails-four-london-user-group.html</link><description><![CDATA[<p>Video of my talk at London User Group about Grails 4</p><p><a href="https://www.youtube.com/watch?v=jxusvoCW_Bw">Go to the linked site</a></p>]]></description><guid>grails-four-london-user-group</guid><pubDate>Fri, 11 Dec 2020 06:41:40 GMT</pubDate></item><item><title>&#x1f5e3; Micronaut Case Study: Novatec</title><link>https://sergiodelamo.com/blog/micronaut-novatec-case-study.html</link><description><![CDATA[<p>I will join <a href="https://twitter.com/barraganc">Carlos Barragan</a>, <a href="https://twitter.com/sda2392">Silvio D'Alessandro</a> from <a href="https://twitter.com/novatecgmbh">Novatec</a> to talk about their experience with Micronaut developing a production application (learning curve, memory consumption, startup ...).</p><div class="h-event">    📅 <time class="dt-start" datetime="2020-12-11 10:00">11<sup>th</sup> December 2020, 10:00 </time> to <time class="dt-end" datetime="2020-12-11 11:30">11:00</time> CST. It is <span class="p-location">Online</span>.   It is free.</p>  </div><blockquote><p>In this 90-minute, complimentary webinar, you'll get a look at the Micronaut® framework in its natural habitat – an actual productive environment where it powers an application that handles several thousand calls per minute!</p></blockquote><p><a href="https://objectcomputing.com/products/micronaut/resources/micronaut-in-action">Register</a></p><p><a href="https://objectcomputing.com/products/micronaut/resources/micronaut-in-action">Go to the linked site</a></p>]]></description><guid>micronaut-novatec-case-study</guid><pubDate>Wed, 02 Dec 2020 10:08:07 GMT</pubDate></item><item><title>Execute a Gradle task shortcut with IntelliJ IDEA</title><link>https://sergiodelamo.com/blog/executing-gradle-task-intellij-shortcut.html</link><description><![CDATA[<p>Most of my projects are Gradle Builds. For such projects, I use in IntelliJ IDEA. However, I delegate Build and Run and tests to Gradle. You can do that at <code>Settings/Preferences | Build, Execution, Deployment |Build Tools | Gradle</code>.</p><p>Thus, running tasks such as test, run, clean or build means executing a Gradle task. Because of that I have a shortcut to execute a Gradle task within IntelliJ IDEA:</p><p><img src="https://images.sergiodelamo.com/execute-gradle-task-shortcut-intellij.png" alt="Custom shortcut to execute a Gradle task in IntelliJ IDEA" /></p><p>Now when I type <code>CONTROL + OPTION + COMMAND + G</code>, I can run the any Gradle task:</p><p><img src="https://images.sergiodelamo.com/execute-gradle-task-shortcut-intellij-running.png" alt="Execute a Gradle task in IntelliJ IDEA" /></p>]]></description><guid>executing-gradle-task-intellij-shortcut</guid><pubDate>Mon, 30 Nov 2020 11:40:21 GMT</pubDate></item><item><title>&#x1f468;&#x1f3fb;‍&#x1f3eb; Teaching Grails Deep Dive</title><link>https://sergiodelamo.com/blog/grails-deep-dive.html</link><description><![CDATA[<p>I teach an online 12 hour Grails course December 7, 9 and 10.</p><p>I work at <a href="https://objectcomputing.com">OCI</a>. OCI is the top contributor to both frameworks <a href="https://grails.org">Grails</a> and <a href="https://micronaut.io">Micronaut</a>. We offer a <a href="https://objectcomputing.com/services/training/schedule/grails-micronaut-combo">combo trainig</a>. I teach a <a href="https://objectcomputing.com/services/training/catalog/grails/grails-deep-dive">Grails Deep Dive</a>.</p><blockquote><p>Outline</p><ul><li>Brief Introduction to Apache Groovy</li><li>Introduction to the Grails framework</li><li>Controllers</li><li>Introduction to GSP</li><li>Dependency injection-&gt;  Introduction to GORM</li><li>GORM data services</li><li>Security</li><li>Testing</li><li>Services</li><li>Micronaut® framework integration</li><li>Plugins</li></ul></blockquote><div class="h-event"><table><thead><tr><th align="left"> </th><th align="left"> </th></tr></thead><tbody><tr><td align="left">📆 Dates</td><td align="left"><time class="dt-start" datetime="2020-12-7 08:00">December 7</span>, December 9, and <time class="dt-end" datetime="2020-12-10 12:00">December 10, 2020</time></td></tr><tr><td align="left">⌛️ Time</td><td align="left">8:00 a.m. to 12:00 p.m. CST</td></tr><tr><td align="left">📍Location</td><td align="left"><span class="p-location">Online</span></td></tr><tr><td align="left">👨🏻‍🏫 Instructor</td><td align="left">Sergio del Amo</td></tr><tr><td align="left">💶 Registration Fee</td><td align="left">$500 USD</td></tr></tbody></table></div><p><a href="https://twitter.com/jeffscottbrown">Jeff Scott Brown</a> teaches the Micronaut course:</p><div class="h-event"><table><thead><tr><th align="left"> </th><th align="left"> </th></tr></thead><tbody><tr><td align="left">📆 Dates</td><td align="left"><time class="dt-start" datetime="2020-12-14 09:00">December 14</span> to <time class="dt-end" datetime="2020-12-17">December 17, 2020</time></td></tr><tr><td align="left">⌛️ Time</td><td align="left">9:00 a.m. to 12:00 p.m. CST</td></tr><tr><td align="left">📍Location</td><td align="left"><span class="p-location">Online</span></td></tr><tr><td align="left">👨🏻‍🏫 Instructor</td><td align="left">Jeff Scott Brown</td></tr><tr><td align="left">💶 Registration Fee</td><td align="left">$500 USD</td></tr></tbody></table></div><p>You can join a single course but if you enroll both, you get a $200 USD off.</p><p><a href="https://objectcomputing.com/services/training/schedule/grails-micronaut-combo">Register</a>!</p><p><a href="https://objectcomputing.com/services/training/schedule/grails-micronaut-combo">Go to the linked site</a></p>]]></description><guid>grails-deep-dive</guid><pubDate>Mon, 30 Nov 2020 05:12:34 GMT</pubDate></item><item><title>&#x1f5e3; London GUG: What's new in Grails 4</title><link>https://sergiodelamo.com/blog/grails4-london-gug.html</link><description><![CDATA[<p>I will be talking Grails at the London Groovy User Group. December 1st, 19:00 - 20:00 CET CET.</p><p><img src="https://images.sergiodelamo.com/grails4londongug.png" alt="Grails 4 talk at the London Groovy User Group" /></p><div class="h-event">  <h1 class="p-name">What's new in Grails 4</h1>  <p>I will be talking <span class="p-summary">Grails at the London Groovy User Group</span>.<br/>     📅 <time class="dt-start" datetime="2020-12-01 19:00">1<sup>st</sup> December 2020, 19:00 CET</time> to <time class="dt-end" datetime="2020-12-01 20:00">20:00</time>. It is <span class="p-location">Online</span>. It is free.</p></div><p><a href="https://www.eventbrite.co.uk/e/london-groovy-and-grails-meetup-whats-new-in-grails-4-tickets-129285914651?aff=speaker">Register</a></p><p><a href="https://www.eventbrite.co.uk/e/london-groovy-and-grails-meetup-whats-new-in-grails-4-tickets-129285914651?aff=speaker">Go to the linked site</a></p>]]></description><guid>grails4-london-gug</guid><pubDate>Fri, 27 Nov 2020 06:37:55 GMT</pubDate></item><item><title>Java Build Benchmarks with Apple M1</title><link>https://sergiodelamo.com/blog/macbook-air-m1-gradle-java-build.html</link><description><![CDATA[<p>I compare an iMac Pro, Macbook Pro, <a href="https://sergiodelamo.com/blog/macbook-air-m1.html">Macbook Air 2020 M1</a> for a common Gradle Java build.</p><h3>iMac Pro 2017</h3><table><thead><tr><th align="left">Characteristic</th><th align="left">Value</th></tr></thead><tbody><tr><td align="left">Model Name</td><td align="left">iMac Pro 2017</td></tr><tr><td align="left">Processor</td><td align="left">3GHz 10-Core Intel Xeon W</td></tr><tr><td align="left">Memory</td><td align="left">32 GB 2666 MHz DDR4</td></tr><tr><td align="left">Total Number of Cores</td><td align="left">10</td></tr><tr><td align="left">Operating System</td><td align="left">Mac OS X 10.15.7 (Mojave)</td></tr></tbody></table><h3>Macbook Pro 2014</h3><table><thead><tr><th align="left">Characteristic</th><th align="left">Value</th></tr></thead><tbody><tr><td align="left">Model Name</td><td align="left">MacBook Pro (Retina, 13-inch, Mid 2014)</td></tr><tr><td align="left">Processor</td><td align="left">3 GHz Dual-Core Intel Core i7</td></tr><tr><td align="left">Memory</td><td align="left">16 GB 1600 MHz DDR3</td></tr><tr><td align="left">Total Number of Cores</td><td align="left">2</td></tr><tr><td align="left">Operating System</td><td align="left">Mac OS X 10.15.7 (Mojave)</td></tr></tbody></table><h3>Macbook Air 2020 M1</h3><table><thead><tr><th align="left">Characteristic</th><th align="left">Value</th></tr></thead><tbody><tr><td align="left">Model Name</td><td align="left">MacBook Air</td></tr><tr><td align="left">Processor</td><td align="left">M1 2020</td></tr><tr><td align="left">Memory</td><td align="left">16 GB</td></tr><tr><td align="left">CPU cores</td><td align="left">8 cores</td></tr><tr><td align="left">Operating system</td><td align="left">Mac OS X 10.16</td></tr></tbody></table><h2>Java version</h2><p>Micronaut is still JDK 8 compatible. I run the tests with Corretto 8 JDK. I installed it via <a href="https://sdkman.io">SDKMan</a>.</p><pre><code>% sdk install java 8.0.275-amzn</code></pre><table><thead><tr><th align="left">Characteristic</th><th align="left">Value</th></tr></thead><tbody><tr><td align="left">Java runtime</td><td align="left">Amazon.com Inc. OpenJDK Runtime Environment 1.8.0_275-b01</td></tr><tr><td align="left">Java VM</td><td align="left">Amazon.com Inc. OpenJDK 64-Bit Server VM 25.275-b01 (mixed mode)</td></tr></tbody></table><h2>Code sample</h2><p>I use <a href="https://github.com/micronaut-projects/micronaut-security">Micronaut Security</a> module.</p><h2>Command</h2><p>I run the Gradle <code>build</code> task which compiles the code, runs the tests and checksytle. It is one of the most common tasks.</p><pre><code>% ./gradlew clean;./gradlew -Dtestcontainers=false build     --no-daemon    --no-build-cache     -x dependencyUpdates     --parallel     --scan</code></pre><p>I supply some arguments:</p><ul><li><p>I use  <code>--no-build-cache</code> because I want to have an easy way to compare between machines. I use <a href="https://docs.gradle.org/current/userguide/build_cache.html">Gradle build cache</a> in my projects. It is a great feature. You should use it.</p></li><li><p>I disable the <a href="https://docs.gradle.org/current/userguide/gradle_daemon.html">Gradle Daemon</a> with <code>--no-daemon</code>. For the same reasons as in the previous item, to compare different machines in the fairest  way.</p></li><li><p>I skip the task <code>dependencyUpdates</code> as it may add inconsistency to the test.</p></li><li><p>I ignore tests which required Docker (which does not run with Apple M1), hence the argument <code>-Dtestcontainers=false</code>.</p></li><li><p>I use <a href="https://docs.gradle.org/nightly/userguide/performance.html#parallel_execution">Gradle Parallel execution</a> with <code>--parallel</code> flag.</p></li></ul><blockquote><p>Most builds consist of more than one project and some of those projects are usually independent of one another. Yet Gradle will only run one task at a time by default, regardless of the project structure (this will be improved soon). By using the --parallel switch, you can force Gradle to execute tasks in parallel as long as those tasks are in different projects.</p></blockquote><h2>Results</h2><table><thead><tr><th align="left">Computer</th><th align="left">Duration</th><th align="left">Gradle Build Scan</th></tr></thead><tbody><tr><td align="left">iMac Pro - 3GHz 10 Core Intel Xeon W</td><td align="left">1m 44s</td><td align="left"><a href="https://gradle.com/s/zvqk5oaaovr3m">Gradle Build Scan</a></td></tr><tr><td align="left">Macbook Air M1 8 Core</td><td align="left">2m 54s</td><td align="left"><a href="https://gradle.com/s/gabwdvawzygoy">Gradle Build Scan</a></td></tr><tr><td align="left">Macbook Pro 2 Core 3GHz I7</td><td align="left">4m 33s</td><td align="left"><a href="https://gradle.com/s/xgunasq3rcfgi">Gradle Build Scan</a></td></tr></tbody></table><p><img src="https://images.sergiodelamo.com/macbook-air-m1-gradle-java-build.png" alt="" /></p><h2>Caveats</h2><p>This is not an empirical comparison. The goal of the post is to put in numbers my feelings. Java builds are fast in a Macbook Air M1.</p><p>These benchmarks should have been calculated with the <a href="https://github.com/gradle/gradle-profiler">Gradle Profiler</a>. However, I was not able to get the Gradle Profiler to run with the M1 computer.</p><p>Don't consider this post as a measurement Micronaut security's build performance.</p><p>Neither, it is intended to showcase of Gradle's performance. It will be unfair for Gradle. I explicitly disabled several features (Build Cache, Daemon) which make Gradle builds much faster.</p><h2>Conclusion</h2><ul><li>My 2017 iMac Pro, a powerhouse, still beats an Macbook Air M1.</li><li>The Macbook Air M1 smokes old Macbook Pros.</li><li>Macbook Air M1 will get you similar Java build times for current top of the line Macbook Pros.</li></ul><p>The exciting thing is that Java builds are not taking full advantage of the transition. It is <a href="https://developer.apple.com/forums/thread/651123">Rosetta emulation</a>:</p><blockquote><p>the Java runtime support will run under Rosetta 2 which will translate Intel compiled code to run on Apple Silicon. The Java Virtual Machine will need to be recompiled for Apple Silicon to fully utilize its power, but Java based applications should continue to run as expected on both Intel and Apple Silicon hardware.</p></blockquote><p>Once the Java story improves in Apple silicon, I will write about it. I am thrilled.</p>]]></description><guid>macbook-air-m1-gradle-java-build</guid><pubDate>Sat, 21 Nov 2020 11:15:00 GMT</pubDate></item><item><title>How to check if an app is Native or Intel in a M1 Mac?</title><link>https://sergiodelamo.com/blog/apple-silicon-native-or-intel.html</link><description><![CDATA[<p>When you first install an Intel app, MacOS prompts you to install Rosetta. However, after the first installation, Rosetta is transparent. Thus, it is difficult to know if an app you installed is a Universal or Intel App.</p><p>A way to check wether an app you installed is native or intel is to right click &quot;Get Info&quot; in an application.</p><p><img src="https://images.sergiodelamo.com/app-get-info.png" alt="" /></p><p>If the app runs natively, it tells you <code>Kind: Application(Universal)</code></p><p><img src="https://images.sergiodelamo.com/macos-app-universal.png" alt="" /></p><p>If the app runs natively, it tells you <code>Kind: Application(Intel)</code></p><p><img src="https://images.sergiodelamo.com/macos-app-intel.png" alt="" /></p>]]></description><guid>apple-silicon-native-or-intel</guid><pubDate>Fri, 20 Nov 2020 06:49:00 GMT</pubDate></item><item><title>MacBook Air M1</title><link>https://sergiodelamo.com/blog/macbook-air-m1.html</link><description><![CDATA[<p>I am an iPad Pro user since 2018. iPad Pro is a hardware wonder. I use the iPad Pro a lot. However, I am a developer. Because of that MacOS caters more to my needs. For years, there has been rumours about Apple transitioning Mac to ARM. I have experienced the power of an iPad Pro first hand and I was excited what could it mean for the Mac and MacOS.</p><p>Last WWDC, Apple announced the transition. I waited eagerly for the new hardware unveil.</p><p>I bought a MacBook Air M1 with 8 Core and 16GB of RAM. It arrived on Tuesday evening. I love it.</p><h2>No Fan</h2><p>My main computer is an iMac Pro. My portable computer for the past couple of years have been an iPad Pro. Both are extremely quiet machines. I rarely hear the iMac Pro fans. Sometimes the fan triggers when I tax the iMac to run the Micronaut Gradle builds with the <code>--parallel</code> flag and I use the iMac's cores to their fullest potential. But in general is an extremely powerful but quiet machine. And I love it because of that.</p><p>Before the iMac Pro, my main machine was a 2014 Macbook Pro. I still own that machine. I rarely use it now, unless I need to do programming outside home. That it is a loud machine. The fans are almost always engaged when I use it. I don't remember if it was always like that. But it is now.</p><p>The Macbook Air M1 does not have a fan. It stays in silence and that it is an awesome feature for a Laptop.</p><h2>Battery</h2><p>Mind blowing. For the battery improvements, it is worth the purchase. I am getting around 8 hours of my normal use (programming, email, browser, image editing, writing) of this computer. I will drain the battery of my 2014 Macbook Pro with the same usage in around 2 hours.</p><h2>Keyboard and Trackpad</h2><p>My move to iMac kept me way from butterfly keyboard era. Thus, I have never been down on Apple's keyboards. The keyboard reminds me of the Magic Keyboard. Backlit is nice. The trackpad biggest change for me. Much bigger than my 2014 Macbook Pro's trackpad.</p><h2>Touch ID</h2><p>I have used TouchID on iPads and iPhones. However, I never owned a Mac with Touch ID until now. Unlocking the computer with Touch ID is nice. But what I am enjoying is Touch ID in combination with <a href="https://1password.com">1Password</a>. Unlocking 1Password is something I do multiple times a day and using my thumb to do it is a snappy feature.</p><h2>Are apps ready for Apple Silicon?</h2><p>Most of the developers of my apps provide already a universal binary. I will write a blog post about the apps I use. The apps which are not yet ready and run seamlessly thanks to Rosetta 2. You don't notice anything.</p><h2>Java Development</h2><p>My work is mostly JVM development. I will continue to do the heavy-lift portions of that job in my iMac Pro connected two big monitors. However, I have installed <a href="https://www.jetbrains.com/idea/">IntelliJ IDEA</a>,  <a href="https://aws.amazon.com/corretto/">AWS Corretto JDK</a> via <a href="https://sdkman.io">SDKMan</a> in my Macbook Air M1 without issues. Same tools I use in my Intel Macs. I built Micronaut Security and it builds fast. I think the Java story will improve dramatically within the next year but even running under Rosetta 2 is not bad. I will write a blog post with Java build comparisons.</p><h2>Incompatible apps</h2><p><a href="https://www.docker.com/products/docker-desktop">Docker Desktop</a> crashes on startup. I use Docker mainly in combination with <a href="https://www.testcontainers.org">Test Containers</a>. Thus, I am running my builds ignoring the tests which require Test Containers. I hope it gets fixed soon. It is a showstopper for many developers.</p><h2>iOS and iPad apps</h2><p>I work with people in three different time zones. Typically, people in St. Louis, London or Madrid. <a href="https://apps.apple.com/us/app/calzones/id1451728473">CalZones</a> is a helpful app for iOS and iPadOS to deal with time zones. It was not in the Mac and now I can running it thanks to Apple Silicon macs.</p><p>I installed <a href="https://overcast.fm">Overcast</a>, my podcast player of choice for iOS and iPad OS. I synced my account, play podcasts. It works fine.</p><p>They look a little bit out of place but I welcome to have them. I need to play more with them to understand how I feel about them.</p><h2>Cold</h2><p>I often use my Laptop in a couch and it was important for me not get hot. My previously laptop got hot pretty fast. This laptop is cold.</p><h2>Ready to go</h2><p>Instant wake is nice. If you used an iPad, you know the appeal. Having the same experience in MacOS reduces the barrier to pick my Mac.</p><h2>Fast</h2><p>This computer is fast. Really fast!. I have used it for a day but I have the same feeling I got when I purchased my iMac Pro. This is how computing should be. Web browsing is blazing fast.</p><p>It does not get hot. It is quiet. It is fast. It is the future. It is now.</p>]]></description><guid>macbook-air-m1</guid><pubDate>Thu, 19 Nov 2020 06:02:00 GMT</pubDate></item><item><title>ApacheCon 2020: Micronaut + Groovy</title><link>https://sergiodelamo.com/blog/apache-conf-2020-microanut-groovy.html</link><description><![CDATA[<p>Slides and videos from my talk at ApacheCon 2020 about Micronaut and Groovy</p><p>Talk at ApacheCon 2020 about Micronaut and Groovy. The team behind Micronaut is the same team behind <a href="https://grails.org">Grails</a>. Thus, there is a deep care and appreciation for Groovy in the core Micronaut development team.</p><p>The deck covers:</p><ul><li>Micronaut Groovy Dependencies</li><li>Routing</li><li>Configuration</li><li>Micronaut + Spock</li><li>Micronaut + Geb</li><li>GORM for Hibernate</li><li>Micronaut GORM Multi-tenancy</li></ul><p><a href="https://www.youtube.com/watch?v=g6CjmRG0vCs">Go to the linked site</a></p><p><a href="https://www.youtube.com/watch?v=g6CjmRG0vCs">Video</a><br/><a href="https://speakerdeck.com/sdelamo/apachecon-2020-micronaut-plus-groovy">Slides</a>></p>]]></description><guid>apache-conf-2020-microanut-groovy</guid><pubDate>Tue, 06 Oct 2020 09:30:00 GMT</pubDate></item><item><title>Micronaut + Azure Functions</title><link>https://sergiodelamo.com/blog/micronaut-and-azure-functions-video.html</link><description><![CDATA[<p>Webinar recording about integrating <a href="https://micronaut.io">Micronaut</a> and <a href="https://docs.microsoft.com/en-us/azure/azure-functions/">Azure Functions</a>. I joined <a href="https://twitter.com/juliendubois">Julien Dubois</a>.</p><p>Read more about <a href="https://micronaut-projects.github.io/micronaut-azure/latest/guide/">Micronaut Azure</a> integration.</p><p><a href="https://www.youtube.com/watch?v=-Kk_PNxdXSk">Go to the linked site</a></p>]]></description><guid>micronaut-and-azure-functions-video</guid><pubDate>Mon, 05 Oct 2020 16:42:25 GMT</pubDate></item><item><title>Groovy Calamari 178</title><link>https://sergiodelamo.com/blog/groovy-calamari-178.html</link><description><![CDATA[<p>[%summary]</p><p>I wrote a new issue of my newsletter:</p><blockquote><p><a href="https://apachecon.com/acna2020/">ApacheCon</a> Groovy Track, Groovy Ecosystem usage report and blogs everywhere.</p></blockquote><p><a href="http://groovycalamari.com/issues/178">Go to the linked site</a></p>]]></description><guid>groovy-calamari-178</guid><pubDate>Wed, 30 Sep 2020 09:30:00 GMT</pubDate></item><item><title>(Public Speaking) Developing AWS Lambda Functions with Micronaut</title><link>https://sergiodelamo.com/blog/cloud-conference-rio-de-jainero.html</link><description><![CDATA[<p>I will be talking at Cloud Conference Rio de Janerio about how to write AWS Lambda functions with Micronaut</p><p>🗓 17 October<br /><a href="https://cloudconferenceday.com/">Registration</a>!</p><p><img src="https://sergiodelamo.com/blog/cloud-conference-rio-de-jainero.jpg" alt="" /></p>]]></description><guid>cloud-conference-rio-de-jainero</guid><pubDate>Wed, 30 Sep 2020 09:30:00 GMT</pubDate></item><item><title>New Micronaut project directly from IntelliJ IDEA wizard</title><link>https://sergiodelamo.com/blog/new-micronaut-project-intellij.html</link><description><![CDATA[<p>Since 2020.2, IntelliJ IDEA Ultimate adds the possibility to create new Micronaut projects directly from the wizard.</p><p><a href="https://micronaut.io/blog/2020-07-30-intellij-idea-new-project-micronaut.html">Go to the linked site</a></p>]]></description><guid>new-micronaut-project-intellij</guid><pubDate>Sun, 27 Sep 2020 09:30:00 GMT</pubDate></item><item><title>Micronaut + Azure online webinar</title><link>https://sergiodelamo.com/blog/upcoming-micronaut-azure-webinar.html</link><description><![CDATA[<p>October 2nd, I will be doing an online Micronaut + Azure Webinar together with Julien Dubois.</p><p><a href="https://twitter.com/juliendubois">Julien Dubois</a> is the Java developer advocacy team manager at Microsoft, lead developer of the JHipster project and a Java Champion. What an honor to share screen with him!</p><blockquote><p>Micronaut Functions and Azure are a productive, end-to-end development experience: build, debug, deploy, and monitor functions seamlessly.</p></blockquote><blockquote><p>In this complimentary technical webinar, Microsoft's Julien Dubois and Micronaut team member Sergio del Amo cover Azure functions and demonstrate how and why you'll benefit from developing them with Micronaut.</p></blockquote><p>🗓October 2, 2020<br />🕰9:00 a.m. CDT<br />⏲60 minutes<br />📍Online</p><p>Moreover, it is free. <a href="https://objectcomputing.com/products/micronaut/resources/azure-functions-with-micronaut">Register</a>!</p>]]></description><guid>upcoming-micronaut-azure-webinar</guid><pubDate>Thu, 24 Sep 2020 09:30:00 GMT</pubDate></item><item><title>Run a Gradle build with Nova editor</title><link>https://sergiodelamo.com/blog/nova-gradle-task.html</link><description><![CDATA[<p>I build this website with <a href="https://gradle.org">Gradle</a>. Posts are written in Markdown. When I run <code>./gradlew build</code>, I build a statically generated site, a collection of HTML, CSS, images, PDFs and javascript files.  I am trying <a href="https://nova.app">Nova</a>, the code editor recently announced by <a href="https://panic.com">Panic</a>. I've been a long time customer of Panic products <a href="https://panic.com/coda/">Coda 2</a>, <a href="https://panic.com/transmit/">Transmit</a>. Thus, I was been eagearly waiting for Nova. So far, I can tell you two things. It feels Mac native and it feels fast.</p><p>I set some custom tasks to build my website.</p><p><a href="https://library.panic.com/nova/run-tasks/">Nova run Tasks</a></p><blockquote><p>Run Tasks provide a highly-configurable interface for running external operations from inside Nova. You can create your own Run Tasks, or they may be provided by Extensions you have installed.</p></blockquote><p><img src="https://images.sergiodelamo.com/nova-app-gradle-build.png" alt="Build Gradle build with Nova" /></p><p><img src="https://images.sergiodelamo.com/blog/nova-app-gradle-clean.png" alt="Clean Gradle build with Nova" /></p><p>I wrote this blog post in Nova, build the site locally, commit the changes and push directly from the editor.</p><p><img src="https://images.sergiodelamo.com/blog/NovaGradle.gif" alt="Clean and Build with Gradle and Nova" /></p>]]></description><guid>nova-gradle-task</guid><pubDate>Tue, 22 Sep 2020 10:07:00 GMT</pubDate></item><item><title>Gradle Podcast plugin</title><link>https://sergiodelamo.com/blog/podcast-gradle-plugin.html</link><description><![CDATA[<p>Last week, I published my first <a href="https://plugins.gradle.org">Gradle plugin</a>. It eases the creation of a Podcast RSS feed. Internally it uses <a href="https://micronaut-projects.github.io/micronaut-rss/latest/guide/index.html#itunespodcast">micronaut-itunespodcast</a> module.</p><p>The plugin has an extension to configure your Podcast metadata</p><pre><code class="language-groovy">podcast {    episodes = file(&quot;episodes&quot;)    title = 'Hiking Treks'    link = 'https://www.apple.com/itunes/podcasts/'    language = 'en-us'    copyright = '&amp;#169; 2020 John Appleseed'    description = 'Love to get outdoors and discover nature&amp;apos;s treasures? Hiking Treks is the show for you. We review hikes and excursions, review outdoor gear and interview a variety of naturalists and adventurers. Look for new episodes each week.'    author = &quot;The Sunset Explorers&quot;    type = 'SERIAL'    owner {        name = &quot;Sunset Explorers&quot;        email = &quot;mountainscape@icloud.com&quot;    }    image {        url = &quot;https://applehosted.podcasts.apple.com/hiking_treks/artwork.png&quot;    }    block = false    categories = 'SPORTS_WILDERNESS'    explicit = false}</code></pre><p>You write your episode show notes in Markdown and use metadata to configure the audio files associated with the episode:</p><p><em>episodes/aae20190425.md</em></p><pre><code class="language-md">---episodeType: TRAILERepisode: 1season: 1title: Hiking Treks TrailerenclosureLength: 498537enclosureType: audio/mpegenclosureUrl: http://example.com/podcasts/everything/AllAboutEverythingEpisode4.mp3guid: aae20190418pubDate: 2019-01-08T01:15:00Z[GMT]duration: 1079explicit: false---The Sunset Explorers share tips, techniques and recommendations for great hikes and adventures around the United States. Listen on [Apple Podcasts](https://www.apple.com/itunes/podcasts/)</code></pre><p>Run <code>./gradlew build</code> and it generates the following RSS feed:</p><pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;rss xmlns:content=&quot;http://purl.org/rss/1.0/modules/content/&quot;      version=&quot;2.0&quot; xmlns:itunes=&quot;http://www.itunes.com/dtds/podcast-1.0.dtd&quot;&gt;    &lt;channel&gt;        &lt;title&gt;Hiking Treks&lt;/title&gt;        &lt;link&gt;https://www.apple.com/itunes/podcasts/&lt;/link&gt;        &lt;image&gt;            &lt;title&gt;Hiking Treks&lt;/title&gt;            &lt;link&gt;https://www.apple.com/itunes/podcasts/&lt;/link&gt;            &lt;url&gt;https://applehosted.podcasts.apple.com/hiking_treks/artwork.png&lt;/url&gt;        &lt;/image&gt;        &lt;description&gt;Love to get outdoors and discover nature&amp;amp;apos;s treasures? Hiking Treks is the show for you. We review hikes and excursions, review outdoor gear and interview a variety of naturalists and adventurers. Look for new episodes each week.&lt;/description&gt;        &lt;language&gt;en-us&lt;/language&gt;        &lt;copyright&gt;&amp;amp;#169; 2020 John Appleseed&lt;/copyright&gt;        &lt;category text=&quot;Sports&quot;&gt;            &lt;category text=&quot;Wilderness&quot;&gt;            &lt;/category&gt;        &lt;/category&gt;        &lt;itunes:author&gt;The Sunset Explorers&lt;/itunes:author&gt;        &lt;itunes:type&gt;serial&lt;/itunes:type&gt;        &lt;itunes:owner&gt;            &lt;itunes:name&gt;Sunset Explorers&lt;/itunes:name&gt;            &lt;itunes:email&gt;mountainscape@icloud.com&lt;/itunes:email&gt;        &lt;/itunes:owner&gt;        &lt;itunes:image href=&quot;https://applehosted.podcasts.apple.com/hiking_treks/artwork.png&quot;&gt;&lt;/itunes:image&gt;        &lt;itunes:category text=&quot;Sports&quot;&gt;            &lt;itunes:category text=&quot;Wilderness&quot;&gt;&lt;/itunes:category&gt;        &lt;/itunes:category&gt;        &lt;itunes:explicit&gt;no&lt;/itunes:explicit&gt;        &lt;itunes:block&gt;no&lt;/itunes:block&gt;    &lt;item&gt;        &lt;title&gt;Hiking Treks Trailer&lt;/title&gt;        &lt;enclosure length=&quot;498537&quot;                   type=&quot;audio/mpeg&quot;                   url=&quot;http://example.com/podcasts/everything/AllAboutEverythingEpisode4.mp3&quot;&gt;&lt;/enclosure&gt;        &lt;guid&gt;aae20190418&lt;/guid&gt;        &lt;itunes:episodeType&gt;trailer&lt;/itunes:episodeType&gt;        &lt;itunes:title&gt;Hiking Treks Trailer&lt;/itunes:title&gt;        &lt;content:encoded&gt;&amp;lt;p&amp;gt;The Sunset Explorers share tips, techniques and recommendations forgreat hikes and adventures around the United States. Listen on &amp;lt;a href=&quot;https://www.apple.com/itunes/podcasts/&quot;&amp;gt;Apple Podcasts&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;        &lt;/content:encoded&gt;        &lt;itunes:duration&gt;1079&lt;/itunes:duration&gt;        &lt;itunes:episode&gt;1&lt;/itunes:episode&gt;        &lt;itunes:season&gt;1&lt;/itunes:season&gt;        &lt;itunes:explicit&gt;no&lt;/itunes:explicit&gt;    &lt;/item&gt;    &lt;/channel&gt;&lt;/rss&gt;</code></pre><p>My goal is to enable podcast creators to generate a RSS feed easily. It should make easy to statically host a Podcast RSS feed. It should help keep Podcasts open.</p><p><a href="https://sdelamo.github.io/podcast-gradle-plugin/index.html">Podcast Gradle Plugin documentation</a></p>]]></description><guid>podcast-gradle-plugin</guid><pubDate>Mon, 21 Sep 2020 05:07:00 GMT</pubDate></item><item><title>Merida (Mexico) JUG - Introduction to Micronaut® framework</title><link>https://sergiodelamo.com/blog/jugmerida-intro-to-micronaut.html</link><description><![CDATA[<p>On July 31 2020, I gave an introduction to Micronaut® framework talk at <a href="https://twitter.com/JugMerida">Merida (Mexico) Java user group</a>. Due to Covid-19, the user group has moved to online events. It is great to create Micronaut content in Spanish. If you want me to talk in your local user group, please <a href="https://sergiodelamo.com/contact.html">contact me</a>.</p><p><a href="https://www.youtube.com/watch?v=qsaI1xzUdLw">Go to the linked site</a></p>]]></description><guid>jugmerida-intro-to-micronaut</guid><pubDate>Sun, 20 Sep 2020 09:30:00 GMT</pubDate></item><item><title>Micronaut Euro Rates Library</title><link>https://sergiodelamo.com/blog/euro-rates.html</link><description><![CDATA[<p>I have published a <a href="https://micronaut.io">Micronaut</a> Java library to consume the <a href="https://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html">Euro foreign exchange reference rates</a> XML feed published by the European Central bank.</p><p>The library has a transitive dependency to <a href="https://micronaut-projects.github.io/micronaut-jackson-xml/latest/guide/index.html">Micronaut Jackson XML</a> and because of that it is able to create a Micronaut Declarative HTTP Client to consume an XML payload such as:</p><pre><code class="language-xml">&lt;gesmes:Envelope xmlns:gesmes=&quot;http://www.gesmes.org/xml/2002-08-01&quot; xmlns=&quot;http://www.ecb.int/vocabulary/2002-08-01/eurofxref&quot;&gt;&lt;gesmes:subject&gt;Reference rates&lt;/gesmes:subject&gt;&lt;gesmes:Sender&gt;&lt;gesmes:name&gt;European Central Bank&lt;/gesmes:name&gt;&lt;/gesmes:Sender&gt;&lt;Cube&gt;&lt;Cube time=&quot;2020-09-02&quot;&gt;&lt;Cube currency=&quot;USD&quot; rate=&quot;1.1861&quot;/&gt;&lt;Cube currency=&quot;JPY&quot; rate=&quot;126.00&quot;/&gt;&lt;Cube currency=&quot;BGN&quot; rate=&quot;1.9558&quot;/&gt;&lt;Cube currency=&quot;CZK&quot; rate=&quot;26.338&quot;/&gt;&lt;Cube currency=&quot;DKK&quot; rate=&quot;7.4412&quot;/&gt;&lt;Cube currency=&quot;GBP&quot; rate=&quot;0.88840&quot;/&gt;&lt;Cube currency=&quot;HUF&quot; rate=&quot;358.77&quot;/&gt;&lt;Cube currency=&quot;PLN&quot; rate=&quot;4.4186&quot;/&gt;&lt;Cube currency=&quot;RON&quot; rate=&quot;4.8423&quot;/&gt;&lt;Cube currency=&quot;SEK&quot; rate=&quot;10.3065&quot;/&gt;&lt;Cube currency=&quot;CHF&quot; rate=&quot;1.0799&quot;/&gt;&lt;Cube currency=&quot;ISK&quot; rate=&quot;164.50&quot;/&gt;&lt;Cube currency=&quot;NOK&quot; rate=&quot;10.4060&quot;/&gt;&lt;Cube currency=&quot;HRK&quot; rate=&quot;7.5335&quot;/&gt;&lt;Cube currency=&quot;RUB&quot; rate=&quot;88.0675&quot;/&gt;&lt;Cube currency=&quot;TRY&quot; rate=&quot;8.7588&quot;/&gt;&lt;Cube currency=&quot;AUD&quot; rate=&quot;1.6171&quot;/&gt;&lt;Cube currency=&quot;BRL&quot; rate=&quot;6.4205&quot;/&gt;&lt;Cube currency=&quot;CAD&quot; rate=&quot;1.5495&quot;/&gt;&lt;Cube currency=&quot;CNY&quot; rate=&quot;8.0976&quot;/&gt;&lt;Cube currency=&quot;HKD&quot; rate=&quot;9.1925&quot;/&gt;&lt;Cube currency=&quot;IDR&quot; rate=&quot;17489.04&quot;/&gt;&lt;Cube currency=&quot;ILS&quot; rate=&quot;3.9901&quot;/&gt;&lt;Cube currency=&quot;INR&quot; rate=&quot;86.8225&quot;/&gt;&lt;Cube currency=&quot;KRW&quot; rate=&quot;1408.07&quot;/&gt;&lt;Cube currency=&quot;MXN&quot; rate=&quot;25.9150&quot;/&gt;&lt;Cube currency=&quot;MYR&quot; rate=&quot;4.9176&quot;/&gt;&lt;Cube currency=&quot;NZD&quot; rate=&quot;1.7541&quot;/&gt;&lt;Cube currency=&quot;PHP&quot; rate=&quot;57.665&quot;/&gt;&lt;Cube currency=&quot;SGD&quot; rate=&quot;1.6153&quot;/&gt;&lt;Cube currency=&quot;THB&quot; rate=&quot;37.095&quot;/&gt;&lt;Cube currency=&quot;ZAR&quot; rate=&quot;19.9154&quot;/&gt;&lt;/Cube&gt;&lt;/Cube&gt;&lt;/gesmes:Envelope&gt;</code></pre><p>and bind it to a JavaBean:</p><pre><code class="language-java">EuroRatesApi api = ...GesmesEnvelope envelope = api.currentReferenceRates().blockingGet()</code></pre><p><a href="https://sdelamo.github.io/eurorates/index.html">Micronaut Euro Rates documentation</a></p>]]></description><guid>euro-rates</guid><pubDate>Thu, 03 Sep 2020 05:07:00 GMT</pubDate></item><item><title>Micronaut® framework 2: AWS Lambda Functions</title><link>https://sergiodelamo.com/blog/micronaut2-aws-lambda.html</link><description><![CDATA[<p>Micronaut® framework 2 brings improvements to the way you write AWS Lambda functions. There are several things to consider (what's is your Lambda trigger, are you able to write a GraalVM Native Image of the function) which define what dependencies do you need, what is your handler, how to mitigate cold startup.</p><p><a href="https://micronaut.io/blog/2020-08-31-micronaut-2-aws-lambda.html">Continue reading on Micronaut Blog</a></p><p>Moreover, I wrote several companion <a href="https://guides.micronaut.io">Micronaut Guides</a>:</p><table><thead><tr><th>Type</th><th>Runtime</th><th>Tutorial</th></tr></thead><tbody><tr><td>Application</td><td>Java 11</td><td>Deploy a Micronaut application to AWS Lambda Java 11 Runtime <a href="https://guides.micronaut.io/mn-application-aws-lambda-java11/guide/index.html">JAVA</a> <a href="https://guides.micronaut.io/mn-application-aws-lambda-java11-kotlin/guide/index.html">KOTLIN</a> <a href="https://guides.micronaut.io/mn-application-aws-lambda-java11-groovy/guide/index.html">GROOVY</a></td></tr><tr><td>Application</td><td>GraalVM Native Image Custom Runtime</td><td>Deploy a Micronaut application as a GraalVM Native Image to AWS Lambda <a href="https://guides.micronaut.io/mn-application-aws-lambda-graalvm/guide/index.html">JAVA</a> <a href="https://guides.micronaut.io/mn-application-aws-lambda-graalvm-kotlin/guide/index.html">KOTLIN</a></td></tr><tr><td>Serverless Function</td><td>Java 11</td><td>Deploy a Serverless Micronaut function to AWS Lambda Java 11 Runtime <a href="https://guides.micronaut.io/mn-serverless-function-aws-lambda/guide/index.html">JAVA</a> <a href="https://guides.micronaut.io/mn-serverless-function-aws-lambda-kotlin/guide/index.html">KOTLIN</a> <a href="https://guides.micronaut.io/mn-serverless-function-aws-lambda-groovy/guide/index.html">GROOVY</a></td></tr><tr><td>Serverless Function</td><td>GraalVM Native Image Custom Runtime</td><td>Deploy a Serverless Micronaut function as a GraalVM Native Image to AWS Lambda <a href="https://guides.micronaut.io/mn-serverless-function-aws-lambda-graalvm/guide/index.html">JAVA</a></td></tr></tbody></table><p><a href="https://micronaut.io/blog/2020-08-31-micronaut-2-aws-lambda.html">Go to the linked site</a></p>]]></description><guid>micronaut2-aws-lambda</guid><pubDate>Tue, 01 Sep 2020 09:30:00 GMT</pubDate></item><item><title>(Public Speaking) Codemotion 2020: Building Chatbots with Micronaut® framework.</title><link>https://sergiodelamo.com/blog/codemotion-2020-chatbots-with-micronaut.html</link><description><![CDATA[<p>I will be talking at the <a href="https://events.codemotion.com/conferences/online/2020/online-tech-conference-spanish-edition/">Codemotion 2020</a>. It will be an online conference.</p><p>I have one talk scheduled:</p><h2>Building chatbots with Micronaut</h2><p>📅 Nov 3,4,5  2020 (Agenda not closed)</p><blockquote><p>During this talk you will learn how to build chat bots with Micronaut.</p></blockquote><blockquote><p>We will focus on Telegram bots. You will learn how to build bots from scratch. You will learn how to send messages, accept commands and react to them. Moreover, you will see how Micronaut Telegram modules streamlines bot development.</p></blockquote><p><a href="https://events.codemotion.com/conferences/online/2020/online-tech-conference-spanish-edition/">Registration is open</a>. There are free tickets and a paid tier for on demand access to the conference videos.</p><p>Yes, I will demo <a href="https://exchangeratesbot.com">@ForeignExchangeRatesBot</a>. A Telegram bot to help you obtain foreign exchange rates published by the European Central Bank.</p>]]></description><guid>codemotion-2020-chatbots-with-micronaut</guid><pubDate>Fri, 28 Aug 2020 09:30:00 GMT</pubDate></item><item><title>Micronaut Java library to consume the Curated API</title><link>https://sergiodelamo.com/blog/curated-micronaut-java-library.html</link><description><![CDATA[<p>I have used <a href="https://curated.co">Curated</a> to publish my newsletter <a href="http://groovycalamari.com">Groovy Calamari</a> since I started it.</p><p>Today, I published a Java library to consume the Curated API. It is built with the Micronaut® framework and you can use it in a Micronaut app or as a standalone library.</p><p>This Java library is built with the <a href="https://micronaut.io">Micronaut® framework</a> and it will allow you to consume the <a href="http://support.curated.co/integrations/getting-started-with-the-curated-api/">Curated API</a>.</p><p>I think it is a good example of <a href="https://docs.micronaut.io/latest/guide/index.html#httpClient">Micronaut's HTTP Client</a> capabilities and how easy is to test with the Micronaut® framework.</p><p>I build with <a href="https://kordamp.org/kordamp-gradle-plugins/">Kordamp Gradle Plugins</a>. I publish it to <a href="https://bintray.com/beta/#/groovycalamari/maven/curatedapi">Bintray</a>. I use <a href="https://github.com/features/actions">Github actions</a> as a CI server. I publish the documentation written with <a href="https://asciidoctor.org/docs/what-is-asciidoc/">Asciidoc</a> to <a href="https://pages.github.com">Github Pages</a>.</p><p>Check the Curated API library <a href="https://github.com/sdelamo/curatedapi">documentation</a> and the <a href="https://github.com/sdelamo/curatedapi">Github Repository</a>.</p>]]></description><guid>curated-micronaut-java-library</guid><pubDate>Thu, 27 Aug 2020 10:07:00 GMT</pubDate></item><item><title>(Public Speaking) ApacheCon: Micronaut + Groovy and Taming your browser with Geb</title><link>https://sergiodelamo.com/blog/apache-conf-2020.html</link><description><![CDATA[<p>I will be talking at the <a href="https://www.apachecon.com/acah2020/tracks/groovy.html">Groovy Track</a> in the online conference <a href="https://www.apachecon.com/acah2020/">ApacheCon</a>.</p><p>I have two talks scheduled:</p><h2>Micronaut + Groovy</h2><p>📅Wednesday, 30 September 2020, 19:35 CEST</p><blockquote><p>In this talk, Micronaut committer, Sergio del Amo introduces the framework and demonstrates how you can take your web application development to the next level with Micronaut features and Groovy succinctness to create powerful applications in the most productive way. It showcases how to use Micronaut with Groovy related technologies such as GORM, Spock, Geb. After this talk you should have an understanding of what Micronaut development with Groovy looks like. No initial knowledge of Micronaut is required.</p></blockquote><h2>Taming your browser with Geb</h2><p>📅Wednesday, 30 September 2020, 20:55 CEST</p><blockquote><p>Geb is a Groovy layer on top of Selenium Webdriver. Geb integrates the Page Object patter, with CSS selectors and several DSLs to empower you to write browser test in an elegant and succinct way. In this beginner talk, Sergio de Amo, will introduce you to Geb and show you how to test a real Web. If you ever used Selenium, Geb opens a world of possibilities.</p></blockquote><p><a href="https://hopin.to/events/apachecon-home">ApacheCon Registration is open</a>. There are free tickets or you can support the conference via donation tickets.</p>]]></description><guid>apache-conf-2020</guid><pubDate>Thu, 27 Aug 2020 09:30:00 GMT</pubDate></item><item><title>Grails Programmer : How to install new Relic in a Grails 3 app?</title><link>https://sergiodelamo.com/blog/grails-tips-how-to-install-new-relic-in-a-grails-3-app.html</link><description><![CDATA[<p>Recently I wanted to install <a href="http://newrelic.com/">new relic</a> in a Grails 3 app which I distribute through Elastic Beanstalk.</p><p>One of the steps described in the new <a href="https://docs.newrelic.com/docs/agents/java-agent/frameworks/aws-elastic-beanstalk-installation-java">relic documentation</a> is:</p><blockquote><p>In your WAR file, add the newrelic.jar and newrelic.yml files to WEB-INF/lib/.</p></blockquote><p>How to do that in a Grails 3 application?</p><p>Create a folder in the root folder of your application called newrelicCopy both files (newrelic.jar and newrelic.yml) to the previously created newrelic folder.Add the next snippet to your build.gradle file:</p><pre><code class="language-groovy">war {    from('newrelic') {        into 'WEB-INF/lib'        include 'newrelic.*'    }}</code></pre><p>Additionally you would probably want to add to the dependencies block in build.gradle the next line:</p><pre><code class="language-groovy">compile &quot;org.grails.plugins:newrelic:3.19.2&quot;</code></pre><p>This will install the Grails newrelic Plugin to your grails app.</p><p>Among other things the plugin will insert an interceptor which names transactions based on your controller/action information.</p><p><code>NewRelicInterceptor</code> - An interceptor matching all requests to automatically name transactions as <code>{controllerName}/{actionName}</code>.</p><p>We are done in the grails side, complete the installation guide and you will have new relic configured.</p>]]></description><guid>grails-tips-how-to-install-new-relic-in-a-grails-3-app</guid><pubDate>Thu, 20 Feb 2020 16:40:00 GMT</pubDate></item><item><title>Micronaut Framework and AWS Webinar</title><link>https://sergiodelamo.com/blog/combining-micronaut-framework-and-aws.html</link><description><![CDATA[<p>I did a joined webinar with Stefano Buliani from AWS about Micronaut framework integration with AWS.</p><blockquote><p>In this complimentary webinar, Sergio del Amo demonstrates how the Micronaut Framework's built-in features enable seamless integration with AWS services.</p></blockquote><blockquote><p>We often refer to the Micronaut framework as “natively cloud-native,” but what does that mean?</p></blockquote><blockquote><p>In this complimentary webinar, Sergio del Amo Caballero demonstrates how easy it is to deploy a Micronaut app to AWS Elastic Beanstalk and explains how to integrate it with other AWS Services, including AWS Cognito, S3, Lambda, RDS, and Route 53.</p></blockquote><blockquote><p>The following topics are covered in this webinar:</p></blockquote><blockquote><ul><li>Deploying a Micronaut app to AWS Elastic Beanstalk</li><li>Uploading files to AWS S3 from a Micronaut app</li><li>Securing an app with AWS Cognito</li><li>Publishing a Micronaut function to AWS Lambda</li><li>Sending an email from a Micronaut app with AWS SES</li><li>Service discovery with Route 53</li><li>Alexa skill support with Micronaut</li></ul></blockquote><blockquote><p>Stefano Buliani works in the Serverless organization at Amazon Web Services helping AWS customers implement new applications that leverage AWS Lambda and Amazon API Gateway. Stefano has been a professional developer for 15 years, primarily focusing on distributed systems and service-oriented architectures using Java, Go, and Rust.</p></blockquote><blockquote><p>Stefano joins Sergio as a guest presenter to help answer questions and provide expert advice from an Amazon perspective throughout the presentation.</p></blockquote><p><a href="https://images.sergiodelamo.com/slide_deck_Micronaut_and_AWS_webinar.pdf">Slides</a></p><p><a href="https://www.youtube.com/watch?v=eJyfI1SksUo">Go to the linked site</a></p>]]></description><guid>combining-micronaut-framework-and-aws</guid><pubDate>Mon, 20 Jan 2020 18:03:59 GMT</pubDate></item><item><title>Grails Programmer : How to output CSV from a Grails 3  Controller</title><link>https://sergiodelamo.com/blog/grails-tips-how-to-output-csv-from-a-grails-3-controller.html</link><description><![CDATA[<pre><code class="language-bash">% grails -versionGrails Version: 3.1.11| Groovy Version: 2.4.7| JVM Version: 1.8.0_45</code></pre><p>Create an app with the default web profile.</p><pre><code class="language-bash">grails create-app csvApplication created at /Users/groovycalamari/Documents/tests/csv</code></pre><p>Enter the grails console</p><pre><code class="language-bash">% cd csv% grais</code></pre><p>Create a domain class</p><pre><code class="language-bash">grails&gt; create-domain-class Book| Created grails-app/domain/csv/Book.groovy| Created src/test/groovy/csv/BookSpec.groovy</code></pre><p>Add a couple of properties to the domain class</p><pre><code class="language-groovy">package cvsclass Book {	String title	String author    static constraints = {    }}</code></pre><p>In grails-app/init/BootStrap.groovy add a couple of domain class instances. The BootStrap init closure runs when the app starts.</p><pre><code class="language-groovy">import csv.*class BootStrap {    def init = { servletContext -&amp;amp;gt;	new Book(title: 'Groovy for Domain-Specific Languages', author: 'Fergal Dearle').save()	new Book(title: 'Programming Groovy 2: Dynamic Productivity for the Java Developer', author: 'Venkat Subramaniam').save()    }    def destroy = {    }}</code></pre><p>Create a controller</p><pre><code>$ grailsEnter a command name to run. Use TAB for completion:e...grails&amp;amp;gt; create-controller Book| Created grails-app/controllers/csv/BookController.groovy| Created src/test/groovy/csv/BookControllerSpec.groovy</code></pre><p>This is the controller content:</p><pre><code class="language-groovy">package csvimport grails.config.Configimport grails.core.support.GrailsConfigurationAwareimport static org.springframework.http.HttpStatus.OKclass BookController implements GrailsConfigurationAware {    String csvMimeType    String encoding    def index() {    	final String filename = 'book.csv'        def lines = Book.findAll().collect { [it.title, it.author].join(';') } as List&amp;amp;lt;String&amp;amp;gt;        def outs = response.outputStream        response.status = OK.value()        response.contentType = &quot;${csvMimeType};charset=${encoding}&quot;;        response.setHeader &quot;Content-disposition&quot;, &quot;attachment; filename=${filename}&quot;        lines.each { String line -&amp;amp;gt;            outs &amp;amp;lt;&amp;amp;lt; &quot;${line}\n&quot;        }        outs.flush()        outs.close()    }    @Override    void setConfiguration(Config co) {        csvMimeType = co.getProperty('grails.mime.types.csv', String, 'text/csv')        encoding = co.getProperty('grails.converters.encoding', String, 'UTF-8')    }}</code></pre><p>Several things about the above code.</p><p>A) I will recommend to put the logic fetching the lines in a Service.B) I am using the mime type and encoding defined in application.yml. Learn more about retrieving config values.C) If you want the file to download you need to setup the Content-disposition header.</p><p>If we run the app and call the controller we will download a CSV file as this:</p><p>CSV is probably the best format to export your data from a Grails App. A CSV file is easy to import in Excel. I was tired of my clients asking me how to import a CSV in Excel. I wrote a post; in Spanish though.</p>]]></description><guid>grails-tips-how-to-output-csv-from-a-grails-3-controller</guid><pubDate>Fri, 20 Sep 2019 05:46:00 GMT</pubDate></item><item><title>Madrid GUG - What's new with Grails 4 &#x1f3a5; &#x1f468;&#x1f3fc;‍&#x1f3eb;</title><link>https://sergiodelamo.com/blog/whats-new-in-grails-4.html</link><description><![CDATA[<p>Slides from my talk at Madrid Groovy User Group</p><p><a href="https://www.youtube.com/watch?v=LrFMeG9uKb4">Go to the linked site</a></p><p><a href="https://www.youtube.com/watch?v=LrFMeG9uKb4">Video</a><br/><a href="https://speakerdeck.com/sdelamo/whats-new-in-grails-4">Slides</a>></p>]]></description><guid>whats-new-in-grails-4</guid><pubDate>Tue, 17 Sep 2019 09:30:00 GMT</pubDate></item><item><title>Northem Quality - Control your browser with Geb</title><link>https://sergiodelamo.com/blog/spanish-control-your-browser-with-geb.html</link><description><![CDATA[<p><a href="https://northemquality.github.io/eventos_pasados.html">Northem Quality Community</a>, an online testing community, invited me to talk about Geb on June 20th.  This is a video of the talk.</p><p>I have a <a href="https://github.com/sdelamo/geb-northemquality">code repository</a> with the sample covered during the talk and a little bit more.</p><p><a href="https://www.youtube.com/watch?v=00bqYXNKH18">Go to the linked site</a></p>]]></description><guid>spanish-control-your-browser-with-geb</guid><pubDate>Thu, 11 Jul 2019 05:46:00 GMT</pubDate></item><item><title>Geb Talk at Northem Quality</title><link>https://sergiodelamo.com/blog/geb-talk-at-northem-quality.html</link><description><![CDATA[<p>I will be doing a Youtube live <a href="https://gebish.org">Geb</a> talk from the <a href="https://northemquality.github.io&quot;&gt;">Northem Quality</a> online community. A community dedicated to testing and engineering.</p><p>📅 June 20th.</p><p>⌛️ 18:30 CET.</p><p>The talk will be in spanish. This is an abstract:<strong>Controla tu navegador con Geb</strong></p><blockquote><p>Geb es una capa de Groovy encima de Selenium WebDriver. Geb integra el patrón de Page Object, con selectores CSS y varios DSLs para escribir tests de navegador de un modo elegante y conciso. En esta charla veremos una demo de como usar Geb para testear la Web de Northem Quality Community. Si alguna vez usaste Selenium, descubrirás un mundo de posibilidades con Geb.</p></blockquote><p><a href="https://youtu.be/00bqYXNKH18">Youtube link</a></p>]]></description><guid>geb-talk-at-northem-quality</guid><pubDate>Wed, 19 Jun 2019 09:30:00 GMT</pubDate></item><item><title>GR8Conf 2019 - Micronaut Security</title><link>https://sergiodelamo.com/blog/gr8eu-2019-micronaut-security.html</link><description><![CDATA[<p>Slides from my talk at GR8Conf</p>]]></description><guid>gr8eu-2019-micronaut-security</guid><pubDate>Tue, 28 May 2019 09:30:00 GMT</pubDate></item><item><title>GR8Conf 2019 - What's new with Grails 4</title><link>https://sergiodelamo.com/blog/gr8conf-whats-new-with-grails-4.html</link><description><![CDATA[<p>Slides from my presentation at GR8Conf 2019 about Grails 4</p>]]></description><guid>gr8conf-whats-new-with-grails-4</guid><pubDate>Mon, 27 May 2019 05:07:00 GMT</pubDate></item><item><title>Pass system property in a Gradle Task</title><link>https://sergiodelamo.com/blog/pass-system-property-in-a-gradle-task.html</link><description><![CDATA[<p>If you created a <a href="https://micronaut.io">Micronaut</a> App with Gradle (the default build tool) used by Micronaut CLI, you may want to start your app in a particular Micronaut environment. You can supply an environment with the system property <code>micronaut.environments</code>. Modify your build.gradle file</p><pre><code class="language-groovy">run {systemProperty &quot;micronaut.environments&quot;, System.getProperty('micronaut.environments')}</code></pre><p>Then you can start the app with:</p><pre><code class="language-bash">$ ./gradlew -Dmicronaut.environments=dev run</code></pre>]]></description><guid>pass-system-property-in-a-gradle-task</guid><pubDate>Mon, 25 Mar 2019 09:00:00 GMT</pubDate></item><item><title>Commit Conf - Acceptance Tests with Geb</title><link>https://sergiodelamo.com/blog/commit-conf-test-de-aceptacion-con-geb-commit-conf-2018.html</link><description><![CDATA[<p>Slides from my talk at Commit Conf 2018</p>]]></description><guid>commit-conf-test-de-aceptacion-con-geb-commit-conf-2018</guid><pubDate>Wed, 19 Dec 2018 14:44:00 GMT</pubDate></item><item><title>Add Google Analytics snippet to multiple .html files recursively with a Groovy Script</title><link>https://sergiodelamo.com/blog/google-analytics-to-multiple-html-files-with-groovy.html</link><description><![CDATA[<p>Scenario: You have a static HTML website. With many .html files. You want to add the Google Analytics tracking snippet to every file. It is easy to create a Groovy script do it. Let me show you.</p><p>Create a build.gradle file which uses the <a href="https://docs.gradle.org/current/userguide/groovy_plugin.html">Groovy Gradle plugin</a> and the <a href="https://docs.gradle.org/current/userguide/application_plugin.html">Gradle Application Plugin</a>.</p><pre><code class="language-groovy">apply plugin: 'groovy'apply plugin: 'application'mainClassName = &quot;demo.Main&quot;repositories {    mavenCentral()}dependencies {    compile 'org.codehaus.groovy:groovy-all:2.4.13'}</code></pre><pre><code class="language-bash">$ mkdir -p src/main/groovy/demo$ touch src/main/groovy/demo/Main.groovy</code></pre><p>Create a file src/main/groovy/demo/Main.groovy</p><pre><code class="language-groovy">package demoimport static groovy.io.FileType.FILESclass Main {    public static void main(def args) {        final String path ='/Users/sdelamo/website'        if ( !new File(path).exists() ) {            println &quot;${path} does not exist&quot;	    return	}        final String code = 'UA-XXXXX-2'        final String codePreffix = 'UA-'        final String analyticsSnippet = &quot;&quot;&quot;&amp;lt;!-- Global site tag (gtag.js) - Google Analytics --&amp;gt;&amp;lt;script async src=&quot;https://www.googletagmanager.com/gtag/js?id=${code}&quot;&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;script&amp;gt;  window.dataLayer = window.dataLayer || [];  function gtag(){dataLayer.push(arguments);}  gtag('js', new Date());  gtag('config', '${code}');&amp;lt;/script&amp;gt;&quot;&quot;&quot;.toString()        final String fileSuffix = '.html'        addGoogleAnalyticsSnippet(path, fileSuffix, codePreffix, analyticsSnippet)    }    private    static addGoogleAnalyticsSnippet(String path, String fileSuffix, String codePreffix, String analyticsSnippet) {        new File(path).eachFileRecurse(FILES) {            if (it.name.endsWith(fileSuffix)) {                if (!(it.text.contains(codePreffix))) {                    println &quot;adding analytics snippet to ${it.name}&quot;                    String text = it.text                    it.text = text.replaceAll('&amp;lt;head&amp;gt;', &quot;&amp;lt;head&amp;gt;\n${analyticsSnippet}&quot;.toString())                } else {                    println &quot;${it.name} already contains Google analytics snippet.&quot;                }            }        }    }}</code></pre><p>Add gradlew wrapper</p><pre><code class="language-bash">$ gradle wrapper</code></pre><p>Run your script; Gradle run task is provided by the Gradle Application plugin</p><pre><code class="language-bash">$ ./gradlew run</code></pre>]]></description><guid>google-analytics-to-multiple-html-files-with-groovy</guid><pubDate>Mon, 29 Jan 2018 08:27:00 GMT</pubDate></item><item><title>Mapping options for an Enum in a Grails Domain class</title><link>https://sergiodelamo.com/blog/mapping-options-for-an-enum-in-a-grails-domain-class.html</link><description><![CDATA[<p>The next example illustrates the different options to map an enum.</p><p>Given the next code:</p><pre><code class="language-groovy">package demoimport groovy.transform.CompileStatic@CompileStaticclass BootStrap {    def init = { servletContext -&amp;amp;gt;        Book.saveAll(            new Book(name: 'Grails 3 - Step by Step',                     status: Status.NOT_SET),            new Book(name: 'Grails 3 - A practical guide to application development',                     status: Status.BORROWED),            new Book(name: 'Falando de Grails',                     status: Status.SOLD),        )    }    def destroy = { }}</code></pre><pre><code class="language-groovy">package demoimport groovy.transform.CompileStatic@CompileStaticenum Status {    NOT_SET(-1),    BORROWED(0),    SOLD(1),    final int id    private Status(int id) { this.id = id }}</code></pre><p>For enumType <code>ordinal</code></p><pre><code class="language-groovy">package democlass Book {    String name    Status status    static mapping = {        status enumType: 'ordinal'    }}</code></pre><p>The enum will be mapped in the database as:</p><p>ID	VERSION	NAME	STATUS1	0	Grails 3 - Step by Step	02	0	Grails 3 - A practical guide to application development	13	0	Falando de Grails	2</p><p>For enumType <code>identity</code></p><pre><code class="language-groovy">package democlass Book {    String name    Status status    static mapping = {        status enumType: 'identity'    }}</code></pre><p>The enum will be mapped in the database as:</p><p>ID	VERSION	NAME	STATUS1	0	Grails 3 - Step by Step	-12	0	Grails 3 - A practical guide to application development	03	0	Falando de Grails	1For enumType <code>string</code></p><pre><code class="language-groovy">package democlass Book {    String name    Status status    static mapping = {        status enumType: 'string'    }}</code></pre><p>The enum will be mapped in the database as:</p><p>ID	VERSION	NAME	STATUS1	0	Grails 3 - Step by Step	NOT_SET2	0	Grails 3 - A practical guide to application development	BORROWED3	0	Falando de Grails	SOLD</p>]]></description><guid>mapping-options-for-an-enum-in-a-grails-domain-class</guid><pubDate>Mon, 18 Sep 2017 10:39:00 GMT</pubDate></item><item><title>How to reference  Grails Plugins deployed to your bintray account</title><link>https://sergiodelamo.com/blog/how-to-reference-grails-plugins-deployed-to-your-bintray-account.html</link><description><![CDATA[<p>Add a new Maven Url entry to your dependencies block:</p><pre><code class="language-groovy">repositories {    mavenLocal()    maven { url &quot;https://repo.grails.org/grails/core&quot; }    maven { url 'http://dl.bintray.com/sdelamo/plugins' }}</code></pre>]]></description><guid>how-to-reference-grails-plugins-deployed-to-your-bintray-account</guid><pubDate>Sun, 06 Aug 2017 06:04:00 GMT</pubDate></item><item><title>Grails War is a runnable JAR</title><link>https://sergiodelamo.com/blog/grails-war-is-a-runnable-jar.html</link><description><![CDATA[<p>Do you know you can run a Grails generated WAR with <code>gradle assemble</code> with <code>java -jar</code>?. Let me show you:</p><pre><code class="language-bash">runnablewar$ grails --version| Grails Version: 3.3.0| Groovy Version: 2.4.11| JVM Version: 1.8.0_121runnablewar$ grails create-app --inplacerunnablewar$ ./gradlew assemble:compileJava NO-SOURCE:compileGroovy:findMainClass:assetCompileProcessing File 2 of 25 - apple-touch-icon.pngProcessing File 1 of 25 - apple-touch-icon-retina.pngProcessing File 3 of 25 - favicon.icoProcessing File 4 of 25 - grails-cupsonly-logo-white.svgProcessing File 5 of 25 - skin/database_add.pngProcessing File 6 of 25 - skin/database_delete.pngProcessing File 7 of 25 - skin/database_edit.pngProcessing File 8 of 25 - skin/database_save.pngProcessing File 9 of 25 - skin/database_table.pngProcessing File 10 of 25 - skin/exclamation.pngProcessing File 11 of 25 - skin/house.pngProcessing File 13 of 25 - skin/shadow.jpgProcessing File 12 of 25 - skin/information.pngProcessing File 14 of 25 - skin/sorted_asc.gifProcessing File 15 of 25 - skin/sorted_desc.gifProcessing File 16 of 25 - spinner.gifProcessing File 17 of 25 - application.jsProcessing File 18 of 25 - bootstrap.jsProcessing File 19 of 25 - jquery-2.2.0.min.jsProcessing File 20 of 25 - application.cssProcessing File 21 of 25 - bootstrap.cssProcessing File 22 of 25 - errors.cssProcessing File 23 of 25 - grails.cssProcessing File 24 of 25 - main.cssProcessing File 25 of 25 - mobile.cssFinished Precompiling Assets:buildProperties:processResources:classes:compileWebappGroovyPages NO-SOURCE:compileGroovyPages:war:bootRepackage:assembleBUILD SUCCESSFULTotal time: 17.741 secsrunnablewar$ java -jar build/libs/runnablewar-0.1.warGrails application running at http://localhost:8080 in environment: production</code></pre>]]></description><guid>grails-war-is-a-runnable-jar</guid><pubDate>Fri, 28 Jul 2017 13:39:00 GMT</pubDate></item><item><title>Debug Grails Plugins load order</title><link>https://sergiodelamo.com/blog/debug-grails-plugins-load-order.html</link><description><![CDATA[<p>Grails documentation talks about Understanding Plugin Load Order and controlling it with the use of loadBefore and loadAfter properties.</p><p>It is easy to debug the plugin load order as well. Add this line to your grails-app/conf/logback.groovy file.</p><p><code>logger('grails.plugins.DefaultGrailsPluginManager', INFO, ['STDOUT'], false)</code></p>]]></description><guid>debug-grails-plugins-load-order</guid><pubDate>Wed, 05 Jul 2017 13:40:00 GMT</pubDate></item><item><title>GR8Conf 2017 - tvOS app development with TVMLKit and Grails</title><link>https://sergiodelamo.com/blog/gr8conf2017-tvos-app-development-using-tvmlkit-and-grails.html</link><description><![CDATA[<p>Slides from my talk at GR8Conf 2017.</p><h2>Talk Outline</h2><blockquote><p>The new Apple TV opens the opportunity to develop apps for a new platform. A platform, TV, where users are still spending most of their free time. Apple TV apps can be developed with TVMLKit and a backend serving TVML, TVJS and Data. This talk shows you how to use Grails 3 as your backend for an Apple TV app.After this talk you will be able to jump into the development for tvOS with Grails in no time.</p></blockquote><p><a href="https://www.youtube.com/watch?v=-g2pppnWAEA">Go to the linked site</a></p><p><a href="https://www.youtube.com/watch?v=-g2pppnWAEA">Video</a><br/><a href="https://speakerdeck.com/sdelamo/tvos-app-development-using-tvmlkit-and-grails-1">Slides</a>></p>]]></description><guid>gr8conf2017-tvos-app-development-using-tvmlkit-and-grails</guid><pubDate>Thu, 01 Jun 2017 09:00:00 GMT</pubDate></item><item><title>GR8Conf 2017 - Grails Multi-Project Builds &amp; Multi tenancy</title><link>https://sergiodelamo.com/blog/gr8conf2017-grails-multi-project-builds-multitenancy.html</link><description><![CDATA[<p>Slides from my talk at GR8Conf 2017.</p><p><a href="https://www.youtube.com/watch?v=Dkdmdw2ZE3E">Go to the linked site</a></p><p><a href="https://www.youtube.com/watch?v=Dkdmdw2ZE3E">Video</a><br/><a href="https://speakerdeck.com/sdelamo/grails-multi-project-builds-multitenancy">Slides</a>></p>]]></description><guid>gr8conf2017-grails-multi-project-builds-multitenancy</guid><pubDate>Thu, 01 Jun 2017 08:32:00 GMT</pubDate></item><item><title>GR8Conf 2017 - Mapping a Tree with GORM and Grails</title><link>https://sergiodelamo.com/blog/gr8conf-2017-mapping-a-tree-with-gorm-and-grails.html</link><description><![CDATA[<p>Slides from my talk at GR8Conf 2017 about Mapping a tree with Grails</p><p>Code samples:</p><ul><li><a href="https://github.com/sdelamo/mappingatreepathenumeration/">Path Enumeration GORM</a></li><li><a href="https://github.com/sdelamo/mappingatreeclosuretable/">Closure Table with GORM</a></li><li><a href="https://github.com/sdelamo/mappingatreeadjacencylist/">Adjancency List with GORM</a></li></ul><p><a href="https://www.youtube.com/watch?v=QROeVT7SkXA">Go to the linked site</a></p><p><a href="https://www.youtube.com/watch?v=QROeVT7SkXA">Video</a><br/><a href="https://speakerdeck.com/sdelamo/mapping-a-tree-with-gorm-and-grails">Slides</a>></p>]]></description><guid>gr8conf-2017-mapping-a-tree-with-gorm-and-grails</guid><pubDate>Thu, 01 Jun 2017 06:55:00 GMT</pubDate></item><item><title>Read a property from gradle.properties with a bash script</title><link>https://sergiodelamo.com/blog/read-a-property-from-gradle-properties-with-a-bash-script.html</link><description><![CDATA[<pre><code>#!/usr/bin/env bashGRADLE_PROPERTIES_FILE=gradle.propertiesfunction getProperty {    PROP_KEY=$1    PROP_VALUE=`cat $GRADLE_PROPERTIES_FILE | grep &quot;$PROP_KEY&quot; | cut -d'=' -f2`    echo $PROP_VALUE}GRAILS_VERSION=$(getProperty &quot;grailsVersion&quot;)echo $GRAILS_VERSION</code></pre>]]></description><guid>read-a-property-from-gradle-properties-with-a-bash-script</guid><pubDate>Tue, 16 May 2017 10:07:00 GMT</pubDate></item><item><title>Ignore a Spock test if running in Travis CI</title><link>https://sergiodelamo.com/blog/ignore-a-spock-test-if-running-in-travis-ci.html</link><description><![CDATA[<p>Thanks to <a href="http://mrhaki.blogspot.com.es/2014/06/spocklight-ignore-specifications-based.html">Spock IgnoreIf annotation</a> it is easy to ignore a specification running in Travis CI.</p><pre><code class="language-groovy">import spock.lang.IgnoreIfimport spock.lang.Specificationclass ClassUnderTestSpec extends Specification {    @IgnoreIf( { System.getenv('TRAVIS') as boolean } )    def &quot;test which fials if run by Travis CI&quot;() {    }</code></pre>]]></description><guid>ignore-a-spock-test-if-running-in-travis-ci</guid><pubDate>Thu, 27 Apr 2017 07:24:00 GMT</pubDate></item><item><title>Preview of Groovy 3</title><link>https://sergiodelamo.com/blog/preview-of-groovy-3.html</link><description><![CDATA[<p>This blog post is a translation of <a href="https://twitter.com/daniel_sun">@daniel_sun</a>'s post, originally <a href="http://blog.sunlan.me/2017/04/15/Groovy-3之新特性预览/">posted in Chinesse</a>.</p><p>The next section will focus on the new parser “Parrot&quot; features coming to Groovy 3. If you wish to be able to get yourself back and forth, follow the steps below to start Groovy 3's groovy console to try these new features:</p><pre><code class="language-groovy">$ git clone https://github.com/danielsun1106/groovy-parser.git$ cd groovy-parser$ ./gradlew groovyConsole</code></pre><h2>New feature preview</h2><h3>Improve the loop statement</h3><p>Prior to Groovy 3, Groovy neither supported the do-while statement nor supported statements that really conform to the Java syntax specification (such as multiple initialization and multiple update expressions). In Groovy 3, complete support for looping statements makes Java background developers smoother over Groovy development. Give some examples:</p><h3>Do-while statement example</h3><pre><code class="language-groovy">int i = 0;do {    i++} while (i &amp;lt; 5)assert i == 5</code></pre><p>An example of a for statement that conforms to the Java syntax specification</p><pre><code class="language-groovy">def result = 0for (int i = 0, j = 0; i &amp;lt; 5 &amp;amp;&amp;amp; j &amp;lt; 5; i = i + 2, j++) {    result += i;    result += j;}assert 9 == result</code></pre><h2>2. Supports Lambda expressions</h2><p>Java 8 introduces the Lambda expression, in order to better compatible with Java syntax, Groovy 3 also added support for the syntax features. Give some examples:</p><h3>Lambda expression example</h3><pre><code class="language-groovy">assert 9 == [1, 2, 3].stream().map(e -&amp;gt; e + 1).reduce(0, (r, e) -&amp;gt; r + e)</code></pre><h2>3. Support method reference (reference) and constructor reference (constructor reference)</h2><p>In addition to introducing Lambda expressions, Java 8 introduces the method reference and the constructor reference, and for the same reason, Groovy 3 adds support for the syntax feature. Give some examples:</p><h3>Method reference</h3><pre><code class="language-groovy">assert ['A', 'B', 'C'] == ['a', 'b', 'c'].stream().map(String::toUpperCase).collect(Collectors.toList())</code></pre><h3>Constructor reference example</h3><pre><code class="language-groovy">assert [1, 2, 3] as String[] == [1, 2, 3].stream().map(String::valueOf).toArray(String[]::new)</code></pre><h2>4. Support for try-with-resources statements</h2><p>Java 7 introduced the automatic resource management mechanism, developers can try-with-resources statement is very easy to complete the management of resources. It is also supported in Groovy 3. Give some examples:</p><h3>Try-with-resources statement example</h3><pre><code class="language-groovy">class Resource implements Closeable {    int resourceId;    static closedResourceIds = [];    public Resource(int resourceId) {        this.resourceId = resourceId;    }    public void close() {        closedResourceIds &amp;lt;&amp;lt; resourceId    }}def a = 1;try (Resource r1 = new Resource(1)) {    a = 2;}assert Resource.closedResourceIds == [1]assert 2 == a</code></pre><h2>5. Support for code blocks</h2><p>There is an infrequent but useful syntax in Java that can easily isolate the scope of the variable, the code block. Groovy 3 also supported it. Give some examples:</p><h3>Code block example</h3><pre><code class="language-groovy">{    def a = 1    a++    assert 2 == a}{    def a = 1    assert 1 == a}</code></pre><h2>6. Support Java-style array initialization</h2><p>In order to better compatible with Java syntax, Groovy 3 added support for Java-style array initialization.</p><h3>Array initialization example</h3><pre><code class="language-groovy">def a = new int[] {1, 2}    assert a[0] == 1    assert a[1] == 2    assert a as List == [1, 2]</code></pre><h2>7. support interface default method (default method)</h2><p>Among the many new features introduced in Java 8, the default method of interface is also a useful feature. Groovy 3 naturally will not omit its support. Give some examples:</p><h3>Default method</h3><pre><code class="language-groovy">interface Greetable {    String name();    default String hello() {        return 'hello'    }    default public String sayHello() {        return this.hello() + ', ' + this.name()    }}class Person implements Greetable {    @Override    public String name() {        return 'Daniel'    }}def p = new Person()assert 'hello, Daniel' == &quot;${p.hello()}, ${p.name()}&quot;assert 'hello, Daniel' == p.sayHello()</code></pre><h2>8. New operator: consistency operator (===,! ==), Elvis assignment (? =),! In,! Instanceof</h2><p>In order to make the Groovy program more concise, Groovy 3 introduced these new operators. Give some examples:</p><p>The consistency operator (===,! ==) exampleassert 'abc' === 'abc'assert 'abc' !== new String('abc')Elvis assignment (? =) Example</p><pre><code class="language-groovy">def a = 2a ?= 1assert a == 2a = nulla ?= 1assert a == 1</code></pre><h3>!in Example</h3><pre><code class="language-groovy">assert 1 !in [2, 3, 4]</code></pre><h3>!instanceof Example</h3><pre><code class="language-groovy">assert 1 !instanceof String</code></pre><h2>9. Support for secure retrieval</h2><p>Groovy has a safe way of doing so, that is, references to. This avoids the occurrence of NullPointerException, but lacks support for secure retrieval of collections and arrays, and Groovy 3 makes up for this shortcoming. Give some examples:</p><h3>Safe search example</h3><pre><code class="language-groovy">String[] array = ['a', 'b'];assert 'b' == array?[1];array?[1] = 'c';assert 'c' == array?[1];array = null;assert null == array?[1];array?[1] = 'c';assert null == array?[1];</code></pre><h2>10. Supports runtime Groovydoc and saves Groovydoc as metadata in the AST node</h2><p>Groovy 3 adds runtime Groovydoc, which simply adds @Groovydoc at the beginning of the Groovydoc content to access the Groovydoc content at run time. If Javadoc is accompanied by Java source code is a reflection of the document, then the run-time Groovydoc is further, it is stored in bytecode, and the program has done a real combination.</p><h3>Runtime Groovydoc example</h3><pre><code class="language-groovy">/** * @Groovydoc * class AA */class AA {    /**     * @Groovydoc     * field SOME_FIELD     */    public static final int SOME_FIELD = 1;    /**     * @Groovydoc     * constructor AA     */    public AA() {    }    /**     * @Groovydoc     * method m     */    public void m() {    }    /**     * @Groovydoc     * class InnerClass     */    class InnerClass {    }}/** * @Groovydoc * annotation BB */@interface BB {}assert AA.class.getAnnotation(groovy.lang.Groovydoc).value().contains('class AA')assert AA.class.getMethod('m', new Class[0]).getAnnotation(groovy.lang.Groovydoc).value().contains('method m')assert AA.class.getConstructor().getAnnotation(groovy.lang.Groovydoc).value().contains('constructor AA')assert AA.class.getField('SOME_FIELD').getAnnotation(groovy.lang.Groovydoc).value().contains('field SOME_FIELD')assert AA.class.getDeclaredClasses().find {it.simpleName.contains('InnerClass')}.getAnnotation(groovy.lang.Groovydoc).value().contains('class InnerClass')assert BB.class.getAnnotation(groovy.lang.Groovydoc).value().contains('annotation BB')</code></pre><p>An example of saving Groovydoc as a metadata in an AST node</p><pre><code class="language-groovy">import org.codehaus.groovy.control.*import static org.apache.groovy.parser.antlr4.GroovydocManager.DOC_COMMENTdef code = '''/** * Groovydoc for hello method * @author Daniel Sun */void hello(String name) {}'''def ast = new CompilationUnit().tap {    addSource 'hello.groovy', code    compile Phases.SEMANTIC_ANALYSIS}.astassert ast.classes[0].methods.grep(e -&amp;gt; e.name == 'hello')[0].nodeMetaData[DOC_COMMENT].contains('Groovydoc for hello method')</code></pre>]]></description><guid>preview-of-groovy-3</guid><pubDate>Mon, 24 Apr 2017 08:34:00 GMT</pubDate></item><item><title>Greach - Grails Interview Questions</title><link>https://sergiodelamo.com/blog/greach-2017-grails-interview-questions.html</link><description><![CDATA[<p>Slides from my talk about Greach 2017.</p>]]></description><guid>greach-2017-grails-interview-questions</guid><pubDate>Fri, 31 Mar 2017 06:07:00 GMT</pubDate></item><item><title>Madrid GUG - tvOS app development using TVMLKit and Grails</title><link>https://sergiodelamo.com/blog/madridgug-tvos-app-development-using-tvmlkit-and-grails.html</link><description><![CDATA[<p>Slides from my talk at Madrid Groovy User Group</p>]]></description><guid>madridgug-tvos-app-development-using-tvmlkit-and-grails</guid><pubDate>Tue, 07 Mar 2017 08:32:00 GMT</pubDate></item><item><title>How to validate an email address in Android</title><link>https://sergiodelamo.com/blog/how-to-validate-an-email-address-in-android.html</link><description><![CDATA[<pre><code class="language-java">String email = &quot;me@email.com&quot;;if ( android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches() ) {// VALID EMAIL}</code></pre>]]></description><guid>how-to-validate-an-email-address-in-android</guid><pubDate>Mon, 06 Feb 2017 07:39:00 GMT</pubDate></item><item><title>How to check if a company is an intracommunitary operator?</title><link>https://sergiodelamo.com/blog/how-to-check-if-a-company-is-an-intracommunitary-operator.html</link><description><![CDATA[<p>The European Commision offers a web application to validate a VAT Number</p><p><img src="https://images.sergiodelamo.com/Napkin-8-11-01-17-8.03.31-AM.png" alt="" /></p>]]></description><guid>how-to-check-if-a-company-is-an-intracommunitary-operator</guid><pubDate>Wed, 11 Jan 2017 07:01:00 GMT</pubDate></item><item><title>My first day at OCI</title><link>https://sergiodelamo.com/blog/my-first-day-at-oci.html</link><description><![CDATA[<p><strong>Yesterday I joined OCI Grails team</strong>. I am thrilled!. What better place to continue developing my Grails career. But let me reflect how I got here.</p><p><strong>6th of July, 2011</strong>. This was the first time I heard about Grails. I was organizing a local meetup called Iniciador Guadalajara. We had the pleasure to be joined by Ángel María. The event's talk followed by networking in the nearest bar. While drinking some beers I met Mamen González. She struck me as a great programmer.</p><p>At that point, I was running my own company developing websites and web applications. On particular, I had developed a Struts 1 web application to manage local municipalities. It helped those entities to manage their website and with local paperwork such as permission forms, taxes etc. I wanted to hire or partner up with someone to help me develop the product further and try to reach more and more local governments. After the event, I decided to contact Mamen to see if she would be interested in joining me. I remember we meet in a Mc Donalds close to where she lived. I explained her the product, what where my plans etc. She, in the most polite way, told me I should check Grails. It should allow me to develop faster and better. She told me there was a guy called Alberto Vilches who knew a lot about Grails and who organized a conference called Greach. Needless to say, Mamen did not join me. Who could blame her for dogging the Struts bullet.</p><p><strong>4th of November, 2011</strong>. I attended my first Greach conference. I have attended every Greach conference since. As promised, I was pleasantly surprised by how much Grails simplified development and how magical it seemed. That conference attendance certainly lit a fire under my belly. I began to check Grails and obsess about it.</p><p>Luckily enough, <strong>early 2012</strong> a client approach me to develop a Web application to sell concert tickets. The project scope was big and I decided to pitch the use of Grails to develop it. They accepted and I got my first paid Grails gig. I was a Grails newbie and I asked for help to Álvaro Sanchez and his company Salenda. They were already pushing the envelope in the Spanish Grails ecosystem back then. That project completed successfully and it is still online and selling tickets. After that, I adopted Grails as my backend tool.</p><p>During the <strong>next years</strong>, I worked in several other Grails 2 projects. I worked in a really interesting VoIP integration project, in a software as service solution to manage boutique hotels and often found myself using Grails as a backend for mobile application projects.</p><p><strong>November 2013</strong>, I joined a startup called Shoptimix. I initially joined as the iOS developer. Early 2015, I became the CTO. My first call was to start using Grails in the backend to speed up development. Our initial plan was to hire someone to help me out in the backend but to hire a good Grails developed and convince him to join a startup turned out to be difficult endeavor. I was still convinced Grails was the right framework for us. So we changed gears and I moved completely to the backend where I spent the past two years developing a Grails 3 backend which powered both iOS and Android applications.</p><p>In <strong>2015 Greach conference</strong>, I attended a marvelous talk by Andres Almiray where he discussed the richness of the Groovy Ecosystem. Again, I went home with an idea which I could not shake off my head. I decided to start writing a newsletter about Groovy which will serve two purposes: a) it will make my personal brand more public and thus allow me to hire more easily in case we need to recruit someone for the startup b) it will force to write and thus learn about Groovy every week.</p><p><strong>17th of April 2015</strong>, I sent my first Groovy Calamari. A weekly newsletter about the Groovy Ecosystem; Grails, Gradle, Geb, Ratpack etc. If you ever need to learn about a topic, force yourself to curate 6-10 links every week. No doubt, It will give you a deeper understanding of a topic. At this moment, I have sent 63 issues with an encouraging opening and click through rate and a decent subscriber base.</p><p><strong>2016 was the year I started speaking in public</strong>. I spoke in London, Warsaw, Copenhagen and Madrid. Speaking in public is another thing I will recommend any developer. It forces you to go deeper in a topic to avoid looking like an idiot. And there is no better learning motivation.</p><p><strong>2017 is the year I join OCI</strong>. What a privilege to work in the same company as Grame Rocher or Jeff Brown or any of the hyper-talented developers they have assembled in the Grails OCI team. Looking forward to the years to come.</p>]]></description><guid>my-first-day-at-oci</guid><pubDate>Tue, 10 Jan 2017 13:10:00 GMT</pubDate></item><item><title>How to find the different Java SDK installed in your MacOS installation</title><link>https://sergiodelamo.com/blog/how-to-find-the-different-java-sdk-installed-in-your-macos-installation.html</link><description><![CDATA[<pre><code>$ /usr/libexec/java_home -VMatching Java Virtual Machines (1):    1.8.0_101, x86_64:	&quot;Java SE 8&quot;	/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home</code></pre>]]></description><guid>how-to-find-the-different-java-sdk-installed-in-your-macos-installation</guid><pubDate>Fri, 23 Dec 2016 11:57:00 GMT</pubDate></item><item><title>Codemotion - Spiders, Webbots and scrapers with Geb</title><link>https://sergiodelamo.com/blog/codemotion-aranas-webbots-y-scrapers-con-geb.html</link><description><![CDATA[<p>Slides from my talk at Codemotion 2016</p>]]></description><guid>codemotion-aranas-webbots-y-scrapers-con-geb</guid><pubDate>Fri, 18 Nov 2016 09:30:00 GMT</pubDate></item><item><title>Groovy Programmer : How to use Groovy default parameter values to implement retry functionality</title><link>https://sergiodelamo.com/blog/groovy-programmer-how-to-use-groovy-default-parameter-values-to-implement-retry-functionality.html</link><description><![CDATA[<p>Lets say you have a class which depends on another component. The component ( for example a remote call ) sometimes fails. You want to execute a method with a number of retries. Groovy supports parameters with default values and we are going to leverage that capability and recursion to achieve a retry functionality.</p><pre><code class="language-groovy">package groovycalamariclass WithRetryService {    def remoteService    String name(int retryAttempts = 2, int waitingMillisecondsBetweenAttempts = 5_000) {        String result = remoteService?.name()        if ( !result &amp;amp;&amp;amp; retryAttempts &amp;gt; 0 ) {            sleep(waitingMillisecondsBetweenAttempts)            return name( (retryAttempts - 1), waitingMillisecondsBetweenAttempts )        }        result    }}</code></pre><p>I have a collaborator which always fails.</p><pre><code class="language-groovy">package groovycalamariclass AlwaysFailsRemoteService {    int numberOfTimesCalled = 0    String name() {        numberOfTimesCalled++        return null    }}</code></pre><p>I've a collaborator which fails the first two times, but works the third time is invoked.</p><pre><code class="language-groovy">package groovycalamariclass FailsTwiceWorksThridTimeRemoteService {    int numberOfTimesCalled = 0    String name() {        if (numberOfTimesCalled &amp;lt; 2) {            numberOfTimesCalled++            return null        }        'Sergio del Amo'    }}</code></pre><p>And here the Spock test to verify it works:</p><pre><code class="language-groovy">package groovycalamariimport spock.lang.Specificationclass WithRetryServiceSpec extends Specification {    def &quot;test retry with a failing service will eventually fail&quot;() {        given:        def service = new WithRetryService()        service.remoteService = new AlwaysFailsRemoteService()        when:        String result = service.name()        then:        !result        service.remoteService.numberOfTimesCalled == 3    }    def &quot;test retry with a service which fails initially will eventually pass&quot;() {        given:        def service = new WithRetryService()        service.remoteService = new FailsTwiceWorksThridTimeRemoteService()        when:        String result = service.name()        then:        result        service.remoteService.numberOfTimesCalled == 2    }}``</code></pre>]]></description><guid>groovy-programmer-how-to-use-groovy-default-parameter-values-to-implement-retry-functionality</guid><pubDate>Wed, 16 Nov 2016 16:16:00 GMT</pubDate></item><item><title>Madrid GUG - Upload Groovy code to AWS Lambda function and API Gateway</title><link>https://sergiodelamo.com/blog/upload-a-groovy-microservice-to-aws-lambda-and-api-gateway.html</link><description><![CDATA[<p>Slides from my talk at Madrid Groovy User Group</p>]]></description><guid>upload-a-groovy-microservice-to-aws-lambda-and-api-gateway</guid><pubDate>Wed, 09 Nov 2016 06:49:00 GMT</pubDate></item><item><title>Madrid GUG - Spiders, Webbots and Scrappers with Geb</title><link>https://sergiodelamo.com/blog/madrid-gug-spiders-webbots-and-scrapers-with-geb-madrid-gug-nov-2016.html</link><description><![CDATA[<p>Slides from my talk at Madrid Groovy User Group.</p><p>Additional slides which show how to deploy a groovy micro service to AWS Lambda and API Gateway</p><script async class="speakerdeck-embed" data-id="68e00b847f274adfaa91a4fa54213efa" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script><p><img src="https://images.sergiodelamo.com/Cwwm-YuWgAAOTIq.jpg-large.jpeg" alt="" /></p><p><img src="https://images.sergiodelamo.com/highres_455897537.jpeg" alt="" /></p><p><a href="https://www.youtube.com/watch?v=19JeVKS0NTU">Go to the linked site</a></p><p><a href="https://www.youtube.com/watch?v=19JeVKS0NTU">Video</a><br/><a href="https://speakerdeck.com/sdelamo/spiders-webbots-and-scrapers-with-geb-madrid-gug-nov-2016">Slides</a>></p>]]></description><guid>madrid-gug-spiders-webbots-and-scrapers-with-geb-madrid-gug-nov-2016</guid><pubDate>Tue, 08 Nov 2016 10:07:00 GMT</pubDate></item><item><title>Grails Programmer: How to run a Grails 3 App with IntelliJ</title><link>https://sergiodelamo.com/blog/grails-programmer-how-to-run-a-grails-3-app-with-intellij.html</link><description><![CDATA[<p>This post explains how to run a Grails 3 App with IntelliJ as described in the <a href="https://player.vimeo.com/video/186362455#t=3m17s">Grails 3 IntelliJ Quickcast</a> by <a href="https://twitter.com/jeffscottbrown">Jeff Scott Brown</a></p><h2>Using Application.groovy</h2><p>It is easy to run a Grails 3 App directly from IntelliJ. Execute the <code>main</code> method in <code>Application.groovy</code>. Just click the green arrow.</p><p>![](Application_groovy_-<em>music</em>-___<em>Developer_tests_music</em>.png]</p><p>Please, make sure you <a href="http://sergiodelamo.es/run-grails-3-app-from-intellij-with-runtime-reloading/">turn off bytecode verification</a> to get runtime reloading</p><h2>Run with Gradle BootRun Task</h2><p>You can open a Terminal within IntelliJ and run the gradle task <code>bootRun</code>. Reloading agent will work using the Gradle wrapper.</p><p><img src="https://images.sergiodelamo.com/Fullscreen_25_10_2016__08_22-1024x822.png" alt="" /></p><h3>Run with Grails Run-app</h3><p>You can execute <code>grails run-app</code> directly from a terminal you open within IntelliJ.</p><p><img src="https://images.sergiodelamo.com/Fullscreen_25_10_2016__08_27.png" alt="" /></p>]]></description><guid>grails-programmer-how-to-run-a-grails-3-app-with-intellij</guid><pubDate>Fri, 28 Oct 2016 06:49:00 GMT</pubDate></item><item><title>How to cleanup your macOS terminal history?</title><link>https://sergiodelamo.com/blog/how-to-cleanup-your-osx-history.html</link><description><![CDATA[<pre><code>$ history -c</code></pre>]]></description><guid>how-to-cleanup-your-osx-history</guid><pubDate>Wed, 12 Oct 2016 10:06:00 GMT</pubDate></item><item><title>Swift vs Groovy - Types</title><link>https://sergiodelamo.com/blog/swift-vs-groovy-types.html</link><description><![CDATA[<h2>Getting started Swift types</h2><ul><li><strong>Int</strong> Integer, whole number that does not containing any floating point information.</li><li><strong>Float</strong> 32bit number with floating point information</li><li><strong>Double</strong> 64bit number with floating point information</li><li><strong>Bool</strong> true or false</li><li><strong>String</strong> represents text</li><li><strong>Character</strong> Single unicode character</li></ul><p>There are more types but those are the most common in Swift.</p><h2>Type Naming</h2><p>Swift Types and the types you defined should be UpperCamelCase. Same in Groovy. However, in Groovy we have access to java primitives short, int , long, float, double, byte, char, boolean etc. Those are lowercase.</p><h2>Type inference</h2><p>If we don't explicitly defined a type both Swift and Groovy will try to infer it.</p><p><strong>Swift</strong></p><pre><code class="language-swift">let i = 42 // Intlet d = 42.5 // Double</code></pre><p><strong>Groovy</strong></p><pre><code class="language-groovy">def i = 42 // java.lang.Integerdef d = 42.5 // java.math.BigDecimal</code></pre><p>Define type explicitly</p><pre><code class="language-swift">let i: Int = 42let d: Double = 42.5</code></pre><p><strong>Groovy</strong></p><pre><code class="language-groovy">Integer i = 42Double d = 42.5</code></pre><p>Type conversion</p><p><strong>Swift</strong></p><pre><code class="language-swift">let d: Double = 42.5let i: Int = Int(d) // 42</code></pre><p><strong>Groovy</strong></p><pre><code class="language-groovy">final Double d = 42.5 // java.lang.Doublefinal Integer i = (Integer)42.5 // 42 java.lang.Integerfinal Integer x = 42.5 as Integer // 42 java.lang.Integer</code></pre><p>as keyword in Swift</p><p>If the default inferred type is not what you are looking for, you convert value to type with the as keyword</p><pre><code class="language-swift">let x = 42 as Double</code></pre>]]></description><guid>swift-vs-groovy-types</guid><pubDate>Tue, 11 Oct 2016 05:05:00 GMT</pubDate></item><item><title>Geb Programmer : How to execute Geb with different browsers</title><link>https://sergiodelamo.com/blog/geb-programmer-how-to-execute-geb-with-different-browsers.html</link><description><![CDATA[<pre><code class="language-bash">$ gradle --version------------------------------------------------------------Gradle 3.1------------------------------------------------------------$ mkdir gwebbot_greach$ cd page gwebbot_greach$ touch &amp;amp;lt;i&amp;amp;gt;build.gradle&amp;amp;lt;/i&amp;amp;gt;</code></pre><p>Add Groovy dependency to build.gradle</p><pre><code class="language-groovy">apply plugin: 'groovy'</code></pre><p>Add Gradle wrapper</p><pre><code class="language-bash">$ gradle wrapper --gradle-version 3.1</code></pre><p>add Geb, Selenium, Drivers and Spock depependencies to build.gradle</p><pre><code class="language-groovy">apply plugin: 'groovy'repositories {    jcenter()}ext {    seleniumVersion = '2.52.0'    phantomJsDriverVersion = '1.2.1'}dependencies {    compile &quot;org.gebish:geb-core:1.0&quot;    compile &quot;org.seleniumhq.selenium:selenium-support:${seleniumVersion}&quot;    compile &quot;org.seleniumhq.selenium:selenium-firefox-driver:${seleniumVersion}&quot;    compile &quot;org.seleniumhq.selenium:selenium-chrome-driver:${seleniumVersion}&quot;    compile(&quot;com.codeborne:phantomjsdriver:$phantomJsDriverVersion&quot;)    compile 'org.spockframework:spock-core:1.0-groovy-2.4'}</code></pre><p>Create a test src/test/groovy/gebwebbot/greach/LegalSpec.groovy</p><pre><code class="language-groovy">package gebwebbot.greach.legalimport geb.Browserimport spock.lang.Specificationclass LegalSpec extends Specification {    def &quot;we are able to fetch the legal information about greach&quot;() {        when:        def browser = new Browser()        browser.go 'http://2017.greachconf.com'        then:        browser.page.find('footer div.credits').text() == 'The Greach Network SL, 2011-2017 - CIF B86412491 - C/Valtravieso, 28023 Madrid (Spain)'    }}</code></pre><p>We are going to create a Geb.Config file to leverage Geb environment sensitivity.</p><p>vi src/test/resources/GebConfig.groovy</p><pre><code class="language-groovy">import org.openqa.selenium.chrome.ChromeDriverimport org.openqa.selenium.firefox.FirefoxDriverimport org.openqa.selenium.phantomjs.PhantomJSDriverenvironments {    firefox {        driver = { new FirefoxDriver() }    }    phantomJs {        driver = { new PhantomJSDriver() }    }    chrome {        driver = { new ChromeDriver() }    }}</code></pre><p>Pass Java system properties from the command-line to the gradle test task. In order to do that modify our build.gradle file as follows:</p><!-- [code language="groovy" highlight="19,20,21"] --><pre><code class="language-groovy">apply plugin: 'groovy'repositories {    jcenter()}ext {    seleniumVersion = '2.52.0'    phantomJsDriverVersion = '1.2.1'}dependencies {    compile &quot;org.gebish:geb-core:1.0&quot;    compile &quot;org.seleniumhq.selenium:selenium-support:${seleniumVersion}&quot;    compile &quot;org.seleniumhq.selenium:selenium-firefox-driver:${seleniumVersion}&quot;    compile &quot;org.seleniumhq.selenium:selenium-chrome-driver:${seleniumVersion}&quot;    compile(&quot;com.codeborne:phantomjsdriver:$phantomJsDriverVersion&quot;)    compile 'org.spockframework:spock-core:1.0-groovy-2.4'}test {    systemProperties System.properties}</code></pre><p>Execute tests with firefox</p><pre><code class="language-bash">./gradlew -Dgeb.env=firefox test</code></pre><p>Execute tests with PhantomJS</p><p>You will need to download the drivers for your operating system in the PhantomJs Website.</p><pre><code class="language-bash">./gradlew -Dgeb.env=phantomJs -Dphantomjs.binary.path=/Users/groovycalamari/Documents/phantomjs-2.1.1-macosx/bin/phantomjs test</code></pre><p>Execute tests with Chrome</p><p>You will need to download the WebDriver for Chrome.</p><pre><code class="language-bash">./gradlew -Dgeb.env=chrome -Dwebdriver.chrome.driver=/Users/groovycalamari/Documents/chromedriver test</code></pre>]]></description><guid>geb-programmer-how-to-execute-geb-with-different-browsers</guid><pubDate>Mon, 10 Oct 2016 05:33:00 GMT</pubDate></item><item><title>Geb Programmer : Obtain current page html source?</title><link>https://sergiodelamo.com/blog/geb-programmer-obtain-current-page-html-source.html</link><description><![CDATA[<p>Geb manual:</p><blockquote><p>Geb builds on the WebDriver browser automation library, which means that Geb can work with any browser that WebDriver can.</p></blockquote><p>With WebDriver is easy to obtain the page source. Lets do a test to verify it.</p><p>Lets start by creating a Gradle project with Groovy</p><pre><code class="language-bash">$ gradle --version------------------------------------------------------------Gradle 3.1------------------------------------------------------------$ mkdir pagesource$ cd page source$ touch &amp;lt;i&amp;gt;build.gradle&amp;lt;/i&amp;gt;</code></pre><p>Add Groovy dependency to build.gradle</p><pre><code class="language-groovy">apply plugin: 'groovy'</code></pre><p>Add Gradle wrapper</p><pre><code class="language-bash">$ gradle wrapper --gradle-version 3.1</code></pre><p>add Geb and Spock depependencies to build.gradle</p><pre><code class="language-groovy">apply plugin: 'groovy'repositories {    jcenter()}dependencies {    compile &quot;org.gebish:geb-core:1.0-rc-1&quot;    compile &quot;org.seleniumhq.selenium:selenium-firefox-driver:2.52.0&quot;    compile &quot;org.seleniumhq.selenium:selenium-support:2.52.0&quot;    compile 'org.spockframework:spock-core:1.0-groovy-2.4'}</code></pre><p>Create a test</p><pre><code class="language-bash">$ vi &amp;lt;i&amp;gt;src/test/groovy/PageSourceSpec.groovy&amp;lt;/i&amp;gt;</code></pre><pre><code class="language-groovy">import spock.lang.Specificationimport geb.Browserclass PageSourceSpec extends Specification {   def &quot;test page source of sergiodeamo.es has a closing &amp;lt;/body&amp;gt; tag&quot;() {	when:        def browser = new Browser()        browser.go 'http://sergiodelamo.es'        then:        browser.driver.pageSource.contains('&amp;lt;/body&amp;gt;')   }}</code></pre><p>If you run the test, it will pass:</p><pre><code class="language-bash">$ ./gradlew test</code></pre>]]></description><guid>geb-programmer-obtain-current-page-html-source</guid><pubDate>Sun, 02 Oct 2016 16:54:00 GMT</pubDate></item><item><title>Grails Programmer : How to unit tests allowed HTTP request methods in a Grails 3 Controller's action?</title><link>https://sergiodelamo.com/blog/grails-tips-how-to-unit-tests-allowed-http-request-methods-in-a-grails-3-controllers-action.html</link><description><![CDATA[<p>I am fan of restricting my Grails 3 Controller's actions to certain HTTP methods.</p><p>If an action does not modify the database let use GET, if an action inserts a new row in the database lets use POST, if an action deletes a row form the database lets use DELETE and so on.</p><p>With Grails allowedMethods it is easy to limit, based on the HTTP request method, the access to a controller's action.</p><p>Grails allowedMethods documentation:</p><p>The allowedMethods property provides a simple declarative syntax to specify which HTTP methods are allowed for your controller actions. By default, all request methods are allowed for all controller actions. The allowedMethods property is optional and only needs to be defined if the controller has actions to be restricted to certain request methods.</p><p>We want to restrict the index action of a controller to only POST requests.</p><pre><code class="language-groovy">package myappimport static org.springframework.http.HttpStatus.OKclass TestController {    static allowedMethods = [index: 'POST']    def index() {        render text:&quot;OK&quot;, status: OK    }}</code></pre><p>And this is the unit test.</p><p>With the aim of separating what am I testing, I normally name these unit tests ControllerName+AllowedMethodsSpec. But you can name your unit test class whatever your want.</p><pre><code class="language-groovy">package myappimport grails.test.mixin.TestForimport spock.lang.Specificationimport spock.lang.Unrollimport static javax.servlet.http.HttpServletResponse.*@TestFor(TestController)class TestControllerAllowedMethodsSpec extends Specification {    @Unroll    def &quot;test TestController.index does not accept #method requests&quot;(String method) {        when:        request.method = method        controller.index()        then:        response.status == SC_METHOD_NOT_ALLOWED        where:        method &amp;amp;lt;&amp;amp;lt; ['PATCH', 'DELETE', 'GET', 'PUT']    }    def &quot;test TestController.index accepts POST requests&quot;() {        when:        request.method = 'POST'        controller.index()        then:        response.status == SC_OK    }}</code></pre>]]></description><guid>grails-tips-how-to-unit-tests-allowed-http-request-methods-in-a-grails-3-controllers-action</guid><pubDate>Wed, 28 Sep 2016 06:55:00 GMT</pubDate></item><item><title>Grails Programmer : How to log from a none Grails Artifact</title><link>https://sergiodelamo.com/blog/grails-tips-how-to-log-from-a-none-grails-artifact.html</link><description><![CDATA[<p>Create a grails app with the rest profile:</p><pre><code>$ grails create-app lognongrailsartifact --profile=rest-api</code></pre><p>Create a controller grails-app/controllers/lognongrailsartifact/TestController.groovy</p><pre><code class="language-groovy">package lognongrailsartifactimport static org.springframework.http.HttpStatus.OKclass TestController {    def index() {    	new NonGrailsArtifact().greet()        render text:&quot;OK&quot;, status: OK    }}</code></pre><p>Create a Groovy Class src/main/groovy/lognongrailsartifact/NonGrailsArtifact.groovy; not a grails artifact.</p><p>Note: Grails artefacts get log auto-injected. You will need to inject it manually in this class.</p><pre><code class="language-groovy">package lognongrailsartifactimport org.apache.commons.logging.LogFactoryclass NonGrailsArtifact {	private static final log = LogFactory.getLog(this)	void greet() {		log.info 'Hi'	}}</code></pre><p>Edit <code>grails-app/conf/logback.groovy</code></p><pre><code class="language-groovy">import grails.util.BuildSettingsimport grails.util.Environment// See http://logback.qos.ch/manual/groovy.html for details on configurationappender('STDOUT', ConsoleAppender) {    encoder(PatternLayoutEncoder) {        pattern = &quot;%level %logger - %msg%n&quot;    }}root(ERROR, ['STDOUT'])logger 'lognongrailsartifact.NonGrailsArtifact', INFO, ['STDOUT']def targetDir = BuildSettings.TARGET_DIRif (Environment.isDevelopmentMode() &amp;amp;amp;&amp;amp;amp; targetDir) {    appender(&quot;FULL_STACKTRACE&quot;, FileAppender) {        file = &quot;${targetDir}/stacktrace.log&quot;        append = true        encoder(PatternLayoutEncoder) {            pattern = &quot;%level %logger - %msg%n&quot;        }    }    logger(&quot;StackTrace&quot;, ERROR, ['FULL_STACKTRACE'], false)}</code></pre><p>Run the app</p><pre><code>grails&amp;amp;gt; run-app| Running application...Grails application running at http://localhost:8080 in environment: development</code></pre><p>Invoke the controller</p><pre><code>$ curl -X &quot;GET&quot; &quot;http://localhost:8080/test&quot; -H &quot;Accept: application/json&quot;</code></pre><p>And you will see the log statement:</p><pre><code>grails&amp;amp;gt; INFO lognongrailsartifact.NonGrailsArtifact - Hi</code></pre><p>Soeren Glasius pointed me to a more elegant way to it with an AST.</p><pre><code class="language-groovy">package lognongrailsartifactimport groovy.util.logging.Log4j@Log4jclass NonGrailsArtifact {    void greet() {        log.info 'Hi'    }}</code></pre><p>An even better approach, as pointed by Alvaro Sanchez, will be to do it with @Slf4j.</p><pre><code>package lognongrailsartifactimport groovy.util.logging.Slf4j@Slf4jclass NonGrailsArtifact {    void greet() {        log.info 'Hi'    }}</code></pre><p>SLF4J vs LOG4J Which one to prefer?. SLF4J is basically an abstraction layer. It is not a logging implementation. It means that if you're writing a library and you use SLF4J, you can give that library to someone else to use and they can choose which logging implementation to use with SLF4J e.g. log4j or the Java logging API</p>]]></description><guid>grails-tips-how-to-log-from-a-none-grails-artifact</guid><pubDate>Fri, 23 Sep 2016 07:13:00 GMT</pubDate></item><item><title>Spock Programmer : Setup / Cleanup Inheritance</title><link>https://sergiodelamo.com/blog/spock-programmer-setup-cleanup-inheritance.html</link><description><![CDATA[<p>Craig Atkinson's talk at GR8Conf US Intro to Spock and Geb claimed the next about Spock Cleanup / Inheritance.</p><p>Might have a hierarchy of test classes for DRY common test codeSpock runs the base class setup firstThen goes down the inheritance chaincleanup is the reverse, starting at test class then going up to base classI want it to double test to make sure I understood.</p><pre><code class="language-groovy">import spock.lang.Specificationabstract class BaseSpec extends Specification {    def setup() { println 'base setup()' }    def cleanup() { println 'base cleanup()' }}class DerivedSpec extends BaseSpec {    def setup() { println 'derived setup()' }    def cleanup() { println 'derived cleanup()' }    def &quot;test setup / cleanup inheritance&quot;() {        when:        println &quot;exectuting test case&quot;        then:        true    }}</code></pre><p>Running the above test prints:</p><pre><code>base setup()derived setup()exectuting test casederived cleanup()base cleanup()</code></pre><p>Really nice. Spock Setup / Cleanup inheritance to aid the creation of a hierarchy of tests classes.</p>]]></description><guid>spock-programmer-setup-cleanup-inheritance</guid><pubDate>Sun, 21 Aug 2016 05:24:00 GMT</pubDate></item><item><title>Geb Programmer: User Agent Spoofing</title><link>https://sergiodelamo.com/blog/geb-programmer-user-agent-spoofing.html</link><description><![CDATA[<p><a href="https://sergiodelamo.com/blog/tag/geb.html"><span class="hashtag">#geb</span></a></p><p>As often happens on the internet there is already a website to answer your question. If you would like to know what’s your user agent just visit whatsmyuseragent.com</p><p><img src="https://images.sergiodelamo.com/Screenshot_27_07_16_15_40.png" alt="" /></p><p>If you run Geb with <a href="http://phantomjs.org/">PhantomJs</a> your user agent will identify you as running PhantomJs. You may be blocked because of that. Fortunately it is easy to change the user agent of PhantomJs driver as illustrated in the next GebConfig.groovy file:</p><pre><code class="language-groovy">import org.openqa.selenium.chrome.ChromeDriverimport org.openqa.selenium.firefox.FirefoxDriverimport org.openqa.selenium.phantomjs.PhantomJSDriverimport org.openqa.selenium.remote.DesiredCapabilities def capabilities = new DesiredCapabilities()String fakeUserAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:37.0) Gecko/20100101 Firefox/37.0'capabilities.setCapability('phantomjs.page.settings.userAgent', fakeUserAgent) environments {     chrome {        driver = { new ChromeDriver() }    }     firefox {        driver = { new FirefoxDriver() }    }     phantomJs {        driver = { new PhantomJSDriver() }    }}</code></pre><p>I've a <a href="https://github.com/sdelamo/gebwebbot_useragent">Github repository</a> with this code example.</p>]]></description><guid>geb-programmer-user-agent-spoofing</guid><pubDate>Wed, 27 Jul 2016 13:52:00 GMT</pubDate></item><item><title>Geb Programmer : How to run Geb with PhantomJS Driver in desktop size</title><link>https://sergiodelamo.com/blog/geb-programmer-how-to-run-geb-with-phantomjs-driver-in-desktop-size.html</link><description><![CDATA[<p>If you are developing your Geb programms with Firefox or Chrome driver, they will probably be running in a traditional desktop resolution. You may be puzzled when your tests fail in PhantomJS driver.</p><h2>Why?</h2><p>After further investigation you will probably realise that the tests are failing because the code you developed will only work when you run the test in desktop resolution. The website you are working against may be using fancy adaptive design tricks with hidden content which may become visible in mobile resolutions and viceversa.</p><h2>Maximize!</h2><p>If you execute Geb with PhantomJS driver, by default, it will run in 400x300. You may want to run it in a desktop resolution. Don't dispare. It is easy. You can tell Geb driver to maximize its window.</p><pre><code class="language-groovy">import geb.Browser....def browser = new Browser(driver: new PhantomJSDriver())println browser.driver.manage().window().size.height // 300println browser.driver.manage().window().size.width // 400browser.driver.manage().window().maximize()println browser.driver.manage().window().size.height // 768println browser.driver.manage().window().size.width // 1366</code></pre>]]></description><guid>geb-programmer-how-to-run-geb-with-phantomjs-driver-in-desktop-size</guid><pubDate>Fri, 17 Jun 2016 06:07:00 GMT</pubDate></item><item><title>Gradle Programmer - Ensure source and target compatibility to a specific version of Java</title><link>https://sergiodelamo.com/blog/gradle-programmer-ensure-source-and-target-compatibility-to-a-specific-version-of-java.html</link><description><![CDATA[<p>I originally saw this tip in an <a href="https://twitter.com/aalmiray">Andrés Almiray</a>'s talk. There is a <a href="https://discuss.gradle.org/t/enforcing-targetcompatibility-when-compiling-java-code-with-the-groovy-plugin/5065/1">Gradle forum thread</a> where it is discussed too.</p><p>Sometimes you want to run your program in a machine with Java 1.6. How to ensure source and target compatibility in a <code>build.gradle</code> file:</p><pre><code class="language-groovy">apply plugin: 'java'apply plugin: 'groovy'repositories {    mavenCentral()}sourceCompatibility = 1.6targetCompatibility = 1.6dependencies {    compile 'org.codehaus.groovy:groovy-all:2.4.7'}tasks.withType(JavaCompile) {    sourceCompatibility = '1.6'    targetCompatibility = '1.6'}</code></pre>]]></description><guid>gradle-programmer-ensure-source-and-target-compatibility-to-a-specific-version-of-java</guid><pubDate>Fri, 17 Jun 2016 05:27:00 GMT</pubDate></item><item><title>Madrid GUG - Build an Android app with Groovy and consume a Wordpress JSON API</title><link>https://sergiodelamo.com/blog/construye-un-app-con-groovy-en-android-que-consume-un-api-json-generado-en-con-wordpress.html</link><description><![CDATA[<p>Slides from my talk at Madrid Groovy User Group</p><p><a href="https://www.youtube.com/watch?v=OLduuM_QV7I">Go to the linked site</a></p><p><a href="https://www.youtube.com/watch?v=OLduuM_QV7I">Video</a><br/><a href="https://speakerdeck.com/sdelamo/construye-un-app-con-groovy-en-android-que-consume-un-api-json-generado-en-con-wordpress">Slides</a>></p>]]></description><guid>construye-un-app-con-groovy-en-android-que-consume-un-api-json-generado-en-con-wordpress</guid><pubDate>Tue, 14 Jun 2016 07:13:00 GMT</pubDate></item><item><title>GR8Conf 2016 - How to create a conference android app with Groovy and Wordpres</title><link>https://sergiodelamo.com/blog/g8rconf-2016-how-to-create-a-conference-android-app-with-groovy-and-wordpress.html</link><description><![CDATA[<p>Slides from my talk at GR8Conf 2016.</p><h2>Talk description</h2><blockquote><p>In this talk Sergio del Amo will show you how to:</p><ul><li>Create conference websites with WordPress custom post types and custom fields</li><li>Use a Groovy Android library to consume your WordPress’s generated JSON API</li><li>Develop a simple Android App with Groovy which shows the conference data.</li></ul><p>After this talk you will be able to jump into development for Android with Groovy and consume easily custom WordPress backends</p></blockquote>]]></description><guid>g8rconf-2016-how-to-create-a-conference-android-app-with-groovy-and-wordpress</guid><pubDate>Fri, 03 Jun 2016 06:49:00 GMT</pubDate></item><item><title>GR8Day - Scraping with Geb</title><link>https://sergiodelamo.com/blog/scraping-with-geb.html</link><description><![CDATA[<p>Slides from my talk at GR8Day Warsaw 2016</p>]]></description><guid>scraping-with-geb</guid><pubDate>Thu, 02 Jun 2016 05:46:00 GMT</pubDate></item><item><title>GR8Conf EU 2016 - Scraping with Geb</title><link>https://sergiodelamo.com/blog/gr8conf-2016-scraping-with-geb.html</link><description><![CDATA[<p>Slides from my talk at GR8Conf.</p><h2>Talk description</h2><blockquote><p>Geb is a wonderful tool for testing your Html pages. However, scraping is an unexplored use case where Geb can shine too.</p></blockquote><blockquote><p>In this talk I will show you different scraping examples powered by Geb, after which you will be able to use Geb beyond functional testing</p></blockquote>]]></description><guid>gr8conf-2016-scraping-with-geb</guid><pubDate>Thu, 02 Jun 2016 05:24:00 GMT</pubDate></item><item><title>How to ignore a Spock feature method in Grails 3 while running on Jenkins</title><link>https://sergiodelamo.com/blog/how-to-ignore-a-spock-feature-method-in-grails-3-while-running-on-jenkins.html</link><description><![CDATA[<p>It is easy to ignore a test only if the test is run in a particular environment thanks to the @IgnoreIf Spock annotation. If you run your tests in Jenkins, it is probable that the tests are being run by a user called jenkins. Thus, you can ignore a test in a Jenkins Job easily:</p><pre><code class="language-groovy">@Integration(applicationClass = Application.class)@Rollbackclass IntegrationSpec extends Specification {    @IgnoreIf({ System.getProperty(&quot;user.name&quot;) == 'jenkins' })    def &quot;test will be ignored in jenkins&quot;() {        expect:        false    }}</code></pre>]]></description><guid>how-to-ignore-a-spock-feature-method-in-grails-3-while-running-on-jenkins</guid><pubDate>Mon, 02 May 2016 08:32:00 GMT</pubDate></item><item><title>GR8Day Warsaw - How to create a conference Android App with Groovy and Wordpress</title><link>https://sergiodelamo.com/blog/how-to-create-a-conference-android-app-with-groovy-and-wordpress.html</link><description><![CDATA[<p>Slides from my talk at GR8Day Warsaw 2016</p><p><a href="https://www.youtube.com/watch?v=MJiwIGv9A5U">Go to the linked site</a></p><p><a href="https://www.youtube.com/watch?v=MJiwIGv9A5U">Video</a><br/><a href="https://speakerdeck.com/sdelamo/how-to-create-a-conference-android-app-with-groovy-and-wordpress">Slides</a>></p>]]></description><guid>how-to-create-a-conference-android-app-with-groovy-and-wordpress</guid><pubDate>Sat, 19 Mar 2016 08:00:00 GMT</pubDate></item><item><title>How to use a Groovy trait to output any class as csv?</title><link>https://sergiodelamo.com/blog/how-to-use-a-groovy-trait-to-output-any-class-as-csv.html</link><description><![CDATA[<p>The next example shows how to use a Trait to output a CSV string for any Class which implements the trait</p><pre><code class="language-groovy">trait TraitAsCSV {   List&amp;amp;lt;String&amp;amp;gt; propertyNames() {        this.metaClass.properties.collect { it.name }.findAll { it != 'class'}    }    String csvHeaders() {        propertyNames().join(delimiter())    }    String asCSV() {        def str = &quot;&quot;        def arr = []        for(def propName in propertyNames()) {            def v = this.&quot;$propName&quot;            arr &amp;amp;lt;&amp;amp;lt;  (v ?: '')        }        arr.join(delimiter())    }    static String delimiter() {     ';'    }}class MeetupMember implements TraitAsCSV {    String name    String locality    String twitter    String facebook    String tumblr    String background    String imageUrl    String website}</code></pre><p>The next Spock test will pass:</p><pre><code class="language-groovy">    def &quot;test trait as csv works&quot;() {        given:        def m = new MeetupMember()        m.name = 'Sergio del Amo'        m.locality = 'Guadalajara'        m.twitter = 'https://twitter.com/sdelamo'        m.facebook = null        m.tumblr = null        m.imageUrl = 'http://photos4.meetupstatic.com/photos/member/c/d/6/b/member_254392587.jpeg'        m.website = 'http://www.meetup.com/es-ES/Warsaw-Groovy-User-Group/members/200767921/'        when:        String csvHeaders = m.csvHeaders()        then:        csvHeaders == 'imageUrl;locality;twitter;tumblr;facebook;background;name;website'        when:        def csv = m.asCSV()        then:        csv == &quot;http://photos4.meetupstatic.com/photos/member/c/d/6/b/member_254392587.jpeg;Guadalajara;https://twitter.com/sdelamo;;;;Sergio del Amo;http://www.meetup.com/es-ES/Warsaw-Groovy-User-Group/members/200767921/&quot;    }</code></pre><p>This Trait can be used for any class which you wish to output as a comma separated value. Useful to export information to be sued in Excel or to import it into another process.</p>]]></description><guid>how-to-use-a-groovy-trait-to-output-any-class-as-csv</guid><pubDate>Tue, 15 Mar 2016 09:30:00 GMT</pubDate></item><item><title>Grails Programmer : How run a Gradle task with a particular Grails 3 environment?</title><link>https://sergiodelamo.com/blog/how-run-a-gradle-task-with-a-particular-grails-environment.html</link><description><![CDATA[<pre><code>gradle -Dgrails.env=prod clean</code></pre>]]></description><guid>how-run-a-gradle-task-with-a-particular-grails-environment</guid><pubDate>Mon, 14 Mar 2016 06:44:00 GMT</pubDate></item><item><title>Grails Programmer : How to change the server url of a Grails 3 App?</title><link>https://sergiodelamo.com/blog/grails-tips-how-to-change-the-server-url-of-a-grails-3-app.html</link><description><![CDATA[<p>Sometimes you need to configure the exact server url (via domain name or ip address) of your Grails app. It is easy do it with a small configuration in your <code>grails-app/conf/application.yml</code></p><pre><code class="language-yaml">---environments:    development:        grails:            serverURL: http://192.168.1.5:8080    test:        grails:            serverURL: http://localhost:8080    production:        grails:            serverURL: http://mydomain.com</code></pre>]]></description><guid>grails-tips-how-to-change-the-server-url-of-a-grails-3-app</guid><pubDate>Wed, 02 Mar 2016 08:00:00 GMT</pubDate></item><item><title>How to use a Trait to encapsulate Spring Security Core functionality in a Grails 3 App?</title><link>https://sergiodelamo.com/blog/how-to-use-a-trait-to-encapsulate-spring-security-core-functionality-in-a-grails-3-app.html</link><description><![CDATA[<pre><code>grails --version| Grails Version: 3.0.14| Groovy Version: 2.4.5| JVM Version: 1.8.0_45$ grails create-app my app| Application created at /Users/groovycalamari/Documents/myapp</code></pre><p>Add one dependency to your build.gradle as shown below to install Spring Security Core Plugin</p><pre><code class="language-groovy">buildscript {    ext {        grailsVersion = project.grailsVersion    }    repositories {        mavenLocal()        maven { url &quot;https://repo.grails.org/grails/core&quot; }    }    dependencies {        classpath &quot;org.grails:grails-gradle-plugin:$grailsVersion&quot;        classpath 'com.bertramlabs.plugins:asset-pipeline-gradle:2.5.0'        classpath &quot;org.grails.plugins:hibernate:4.3.10.5&quot;    }}plugins {    id &quot;io.spring.dependency-management&quot; version &quot;0.5.4.RELEASE&quot;}version &quot;0.1&quot;group &quot;myapp&quot;apply plugin: &quot;spring-boot&quot;apply plugin: &quot;war&quot;apply plugin: &quot;asset-pipeline&quot;apply plugin: 'eclipse'apply plugin: 'idea'apply plugin: &quot;org.grails.grails-web&quot;apply plugin: &quot;org.grails.grails-gsp&quot;ext {    grailsVersion = project.grailsVersion    gradleWrapperVersion = project.gradleWrapperVersion}assets {    minifyJs = true    minifyCss = true}repositories {    mavenLocal()    maven { url &quot;https://repo.grails.org/grails/core&quot; }}dependencyManagement {    imports {        mavenBom &quot;org.grails:grails-bom:$grailsVersion&quot;    }    applyMavenExclusions false}dependencies {    compile &quot;org.springframework.boot:spring-boot-starter-logging&quot;    compile &quot;org.springframework.boot:spring-boot-starter-actuator&quot;    compile &quot;org.springframework.boot:spring-boot-autoconfigure&quot;    compile &quot;org.springframework.boot:spring-boot-starter-tomcat&quot;    compile &quot;org.grails:grails-dependencies&quot;    compile &quot;org.grails:grails-web-boot&quot;    compile &quot;org.grails.plugins:hibernate&quot;    compile &quot;org.grails.plugins:cache&quot;    compile &quot;org.hibernate:hibernate-ehcache&quot;    compile &quot;org.grails.plugins:scaffolding&quot;    runtime &quot;org.grails.plugins:asset-pipeline&quot;    testCompile &quot;org.grails:grails-plugin-testing&quot;    testCompile &quot;org.grails.plugins:geb&quot;    // Note: It is recommended to update to a more robust driver (Chrome, Firefox etc.)    testRuntime 'org.seleniumhq.selenium:selenium-htmlunit-driver:2.44.0'    console &quot;org.grails:grails-console&quot;    compile 'org.grails.plugins:spring-security-core:3.0.3'}task wrapper(type: Wrapper) {    gradleVersion = gradleWrapperVersion}</code></pre><p>Lets create the security-related domain classes:</p><pre><code>grails s2-quickstart myapp User Role| Creating User class 'User' and Role class 'Role' in package 'myapp'| Rendered template Person.groovy.template to destination grails-app/domain/myapp/User.groovy| Rendered template Authority.groovy.template to destination grails-app/domain/myapp/Role.groovy| Rendered template PersonAuthority.groovy.template to destination grails-app/domain/myapp/UserRole.groovy|************************************************************* Created security-related domain classes. Your            ** grails-app/conf/application.groovy has been updated with ** the class names of the configured domain classes;        ** please verify that the values are correct.               *************************************************************</code></pre><p>Add a default user to grails-app/init/BootStrap.groovy</p><pre><code class="language-groovy">import myapp.*class BootStrap {    def springSecurityService    def init = { servletContext -&amp;gt;        def userRole = new Role('ROLE_USER').save()        def me = new User('me@sergiodelamo.com', 'groovycalamari').save()        UserRole.create me, userRole        UserRole.withSession {            it.flush()            it.clear()        }    }    def destroy = {    }}</code></pre><p>Create a Controller which will return the name of the logged user:</p><pre><code>grails create-controller WhoAmI| Created grails-app/controllers/myapp/WhoAmIController.groovy| Created src/test/groovy/myapp/WhoAmIControllerSpec.groovy</code></pre><p>The controller code could be something like this:</p><pre><code class="language-groovy">package myappimport grails.plugin.springsecurity.annotation.Securedclass WhoAmIController {    def springSecurityService    @Secured('ROLE_USER')    def index() {        render springSecurityService.principal.username    }}</code></pre><p>If we start the app and hit the controller endpoint, after login, we would see the user's email:</p><p>Probably you would need to user similar Spring Security Code in almost every controller. Most of the time the domain classes will be associated to a user and you need to know who is logged in. Adding the same code over and over is a bad smell.</p><p>If you never used Traits you will probably think about a creating an abstract class and make your controllers inherit from it. Fortunately Groovy Traits offer a much better way to encapsulate this functionality. Lets create a Trait to encapsulate the Spring Security Core Functionality.</p><pre><code>$ mkdir src/main/groovy/mapp</code></pre><p>Lets create a file: src/main/groovy/mapp/TraitSCC.groovy</p><p>with contents:</p><pre><code class="language-groovy">package myapptrait TraitSCC {    def springSecurityService    def currentUsername() {        springSecurityService.principal?.username    }}</code></pre><p>Now lets modify the controller to implement this Trait</p><pre><code class="language-groovy">package myappimport grails.plugin.springsecurity.annotation.Securedclass WhoAmIController implements TraitSCC {    @Secured('ROLE_USER')    def index() {        render currentUsername()    }}</code></pre><p>If you run the app and hit the endpoint you will get the same output as before.</p><p>Ok, the Trait use is a great encapsulation of a behaviour but I still need to remember to add the implements TraitSCC code to every controller in oder to access the functionality.</p><p>Well we can do better. We can use the @Enhances annotation to make every controller implement a trait.</p><!--[code language="groovy" highlight="3,4,6"]--><pre><code class="language-groovy">package myappimport grails.artefact.Enhancesimport org.grails.core.artefact.ControllerArtefactHandler@Enhances(ControllerArtefactHandler.TYPE)trait TraitSCC {        def springSecurityService    def currentUsername() {                springSecurityService.principal?.username    }}</code></pre><p>Remove the implements part from the controller:</p><pre><code class="language-groovy">package myappimport grails.plugin.springsecurity.annotation.Securedclass WhoAmIController {    @Secured('ROLE_USER')    def index() {        render currentUsername()    }}</code></pre><p>If you run the app and hit http://localhost:8080/whoAmI you will get the email back as before.</p><p>Note: In previous versions of Grails I had to move the Traits files with the @Enhances annotation to a grails plugin and reference the plugin from the main project. It did not work if you have enhanced classes in the main project.</p>]]></description><guid>how-to-use-a-trait-to-encapsulate-spring-security-core-functionality-in-a-grails-3-app</guid><pubDate>Sat, 27 Feb 2016 08:00:00 GMT</pubDate></item><item><title>Grails Programmer : How to change the default port of a Grails 3 App</title><link>https://sergiodelamo.com/blog/grails-tips-how-to-change-the-default-port-of-a-grails-3-app.html</link><description><![CDATA[<p>By default a Grails 3 app will start in the port 8080. It is easy however to configure a different port. Edit <code>grails-app/conf/application.yml</code> and add the next snippet at the bottom of the file.</p><pre><code>---server:  port: 8090</code></pre><p>Next time you run the app, it will listen in the port 8090.</p>]]></description><guid>grails-tips-how-to-change-the-default-port-of-a-grails-3-app</guid><pubDate>Sat, 27 Feb 2016 05:07:00 GMT</pubDate></item><item><title>How to log SQL statements in a Grails 3 app</title><link>https://sergiodelamo.com/blog/log-sql-grails-3-app.html</link><description><![CDATA[<pre><code>| Grails Version: 3.1.1| Groovy Version: 2.4.5| JVM Version: 1.8.0_45$ grails create-app myapp| Application created at /Users/shoptimix/Documents/tests/springsecurityexample/myapp$ cd myapp$ grailsgrails&amp;gt; create-domain-class ProductAnnouncement| Created grails-app/domain/myapp/ProductAnnouncement.groovy| Created src/test/groovy/myapp/ProductAnnouncementSpec.groovy</code></pre><p>I add a message field to my domain class since I need to announce stuff.</p><pre><code class="language-groovy">package myappclass ProductAnnouncement {    String message    static constraints = {    }}</code></pre><p>Add some ProductAnnouncement messages when the application starts.To do that modify grails-app/init/BootStrap.groovy</p><pre><code class="language-groovy">import myapp.*class BootStrap {    def init = { servletContext -&amp;gt;	new ProductAnnouncement(message: 'Launch day').save()    }    def destroy = {    }}</code></pre><p>Now If I start the app, I don't see any SQL output. I guess the .save() call did an insert but I have no visibility.</p><pre><code>| Running application...Grails application running at http://localhost:8080 in environment: development</code></pre><p>How can I log the generated SQL statement?</p><p>It is easy. You just need to modify grails-app/conf/application.yml and add a logSql:true line</p><!--[code highlight="19" language="groovy"]--><pre><code class="language-groovy">---hibernate:    cache:        queries: false        use_second_level_cache: true        use_query_cache: false        region.factory_class: 'org.hibernate.cache.ehcache.EhCacheRegionFactory'dataSource:    pooled: true    jmxExport: true    driverClassName: org.h2.Driver    username: sa    password:environments:    development:        dataSource:            logSql: true            dbCreate: create-drop            url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE    test:        dataSource:            dbCreate: update            url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE    production:        dataSource:            dbCreate: update            url: jdbc:h2:./prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE            properties:                jmxEnabled: true                initialSize: 5                maxActive: 50                minIdle: 5                maxIdle: 25                maxWait: 10000                maxAge: 600000                timeBetweenEvictionRunsMillis: 5000                minEvictableIdleTimeMillis: 60000                validationQuery: SELECT 1                validationQueryTimeout: 3                validationInterval: 15000                testOnBorrow: true                testWhileIdle: true                testOnReturn: false                jdbcInterceptors: ConnectionState                defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED------grails:    profile: web    codegen:        defaultPackage: myapp    spring:        transactionManagement:            proxies: falseinfo:    app:        name: '@info.app.name@'        version: '@info.app.version@'        grailsVersion: '@info.app.grailsVersion@'spring:    groovy:        template:            check-template-location: false---grails:    mime:        disable:            accept:                header:                    userAgents:                        - Gecko                        - WebKit                        - Presto                        - Trident        types:            all: '*/*'            atom: application/atom+xml            css: text/css            csv: text/csv            form: application/x-www-form-urlencoded            html:              - text/html              - application/xhtml+xml            js: text/javascript            json:              - application/json              - text/json            multipartForm: multipart/form-data            pdf: application/pdf            rss: application/rss+xml            text: text/plain            hal:              - application/hal+json              - application/hal+xml            xml:              - text/xml              - application/xml    urlmapping:        cache:            maxsize: 1000    controllers:        defaultScope: singleton    converters:        encoding: UTF-8    views:        default:            codec: html        gsp:            encoding: UTF-8            htmlcodec: xml            codecs:                expression: html                scriptlets: html                taglib: none                staticparts: noneendpoints:    jmx:        unique-names: true</code></pre><p>Now when you run the app, it will log the SQL statements:</p><pre><code>$ grails run-app| Running application...Hibernate: drop table product_announcement if existsHibernate: create table product_announcement (id bigint generated by default as identity, version bigint not null, message varchar(255) not null, primary key (id))Hibernate: insert into product_announcement (id, version, message) values (null, ?, ?)Grails application running at http://localhost:8080 in environment: development</code></pre><p>if you modify your hibernate block in grails-app-/conf/application.yml as follows:</p><!-- [code highlight="3"] --><pre><code class="language-yaml">---hibernate:    format_sql: true    cache:        queries: false        use_second_level_cache: true        use_query_cache: false        region.factory_class: 'org.hibernate.cache.ehcache.EhCacheRegionFactory'</code></pre><p>You output will look much more readable:</p><pre><code>Hibernate:    drop table product_announcement if existsHibernate:    create table product_announcement (        id bigint generated by default as identity,        version bigint not null,        message varchar(255) not null,        primary key (id)    )Hibernate:    insert    into        product_announcement        (id, version, message)    values        (null, ?, ?)Grails application running at http://localhost:8080 in environment: development</code></pre><p>Do you want to see values instead of question marks?</p><p>Modify <code>grails-app/conf/logback.groovy</code>:</p><!-- [code language="groovy" highlight="11, 12"] --><pre><code class="language-groovy">import grails.util.BuildSettingsimport grails.util.Environment// See http://logback.qos.ch/manual/groovy.html for details on configurationappender('STDOUT', ConsoleAppender) {    encoder(PatternLayoutEncoder) {        pattern = &quot;%level %logger - %msg%n&quot;    }}logger 'org.hibernate.type.descriptor.sql.BasicBinder', TRACE, ['STDOUT']logger 'org.hibernate.SQL', TRACE, ['STDOUT']root(ERROR, ['STDOUT'])def targetDir = BuildSettings.TARGET_DIRif (Environment.isDevelopmentMode() &amp;amp;&amp;amp; targetDir) {    appender(&quot;FULL_STACKTRACE&quot;, FileAppender) {        file = &quot;${targetDir}/stacktrace.log&quot;        append = true        encoder(PatternLayoutEncoder) {            pattern = &quot;%level %logger - %msg%n&quot;        }    }    logger(&quot;StackTrace&quot;, ERROR, ['FULL_STACKTRACE'], false)}</code></pre><p>And if you rerun the app you will see:</p><pre><code>DEBUG org.hibernate.SQL -    insert    into        product_announcement        (id, version, message)    values        (null, ?, ?)Hibernate:    insert    into        product_announcement        (id, version, message)    values        (null, ?, ?)TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - [0]TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - [Launch day]Grails application running at http://localhost:8080 in environment: development</code></pre><p>The last part of this post is improved thanks to the post Grails Hibernate Logging by Dan Vega</p><p>Logging SQL can help you to optimise single queries and also it will warn you if you execute to many queries to the database while performing a single task.</p>]]></description><guid>log-sql-grails-3-app</guid><pubDate>Wed, 24 Feb 2016 09:20:00 GMT</pubDate></item><item><title>Grails Programmer : How to change the default port where your Grails App runs?</title><link>https://sergiodelamo.com/blog/grails-tips-how-to-change-the-default-port-where-your-grails-app-runs.html</link><description><![CDATA[<p>If you create a Grails 3 app and run it; it will start at port 8080.</p><pre><code>$  grails --versionPicked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 -Duser.language=en -Duser.region=US| Grails Version: 3.0.14| Groovy Version: 2.4.5| JVM Version: 1.8.0_45$  grails create-app testnewport| Application created at /Users/groovycalamari/Documents/tests/testnewport$ cd testnewport$ grails run-appGrails application running at http://localhost:8080 in environment: development</code></pre><p>If you want to start it at a different port add to your application.yml the next block:</p><pre><code class="language-groovy">---server: port: 28080</code></pre><p>And voila:</p><pre><code>Grails application running at http://localhost:28080 in environment: development</code></pre>]]></description><guid>grails-tips-how-to-change-the-default-port-where-your-grails-app-runs</guid><pubDate>Tue, 23 Feb 2016 14:44:00 GMT</pubDate></item><item><title>Grails Programmer : How to customize the war name generated by "grails war" command?</title><link>https://sergiodelamo.com/blog/grails-tips-how-to-customize-the-war-name-generated-by-grails-war-command.html</link><description><![CDATA[<pre><code>$ grails --version| Grails Version: 3.1.1| Groovy Version: 2.4.5| JVM Version: 1.8.0_45</code></pre><pre><code>$ grails create-app myapp| Application created at /Users/sdelamo/Documents/test/myapp</code></pre><p>If I run:</p><pre><code>$ cd my app$ grails war| Built application to build/libs using environment: production</code></pre><p>Note how the grails war command uses by default the production environment. See the generated output:</p><pre><code>$ ls -la build/libstotal 235648drwxr-xr-x   4 softamo  staff       136 Feb 20 17:30 .drwxr-xr-x  13 softamo  staff       442 Feb 20 17:30 ..-rw-r--r--   1 softamo  staff  63561794 Feb 20 17:30 myapp-0.1.war-rw-r--r--   1 softamo  staff  57083136 Feb 20 17:30 myapp-0.1.war.original</code></pre><p>I can explicitly tell the command to use a different environment</p><pre><code>$ cd my app$ grails -Dgrails.env=test war| Built application to build/libs using environment: test</code></pre><p>The generated war file uses the same naming:</p><pre><code>$ ls -la build/libstotal 235648drwxr-xr-x   4 softamo  staff       136 Feb 20 17:32 .drwxr-xr-x  13 softamo  staff       442 Feb 20 17:30 ..-rw-r--r--   1 softamo  staff  63561790 Feb 20 17:32 myapp-0.1.war-rw-r--r--   1 softamo  staff  57083132 Feb 20 17:32 myapp-0.1.war.original</code></pre><p>I want to change the default naming slightly. I want to add the environment to the name. When the user does not pass an environment, the name should reflect the production environment; the default environment.</p><p>It is easy to achieve. Just add the next two lines to the bottom of your build.gradle (or at least after apply plugin 'war')</p><pre><code class="language-groovy">String env = System.getProperty('grails.env') ?: 'prod'war.baseName &quot;${rootProject.name}-${env}&quot;</code></pre><p>And voila:</p><pre><code class="language-groovy">$ grails clean;grails -Dgrails.env=test war| Built application to build/libs using environment: test$ ls -la build/libstotal 471296drwxr-xr-x   6 softamo  staff       204 Feb 20 17:37 .drwxr-xr-x  13 softamo  staff       442 Feb 20 17:30 ..-rw-r--r--   1 softamo  staff  63561790 Feb 20 17:37 myapp-test-0.1.war-rw-r--r--   1 softamo  staff  57083132 Feb 20 17:37 myapp-test-0.1.war.original</code></pre><p>droggo, a user in the <a href="http://slack-signup.grails.org/">Grails Slack channel</a> pointed me to this solution.</p>]]></description><guid>grails-tips-how-to-customize-the-war-name-generated-by-grails-war-command</guid><pubDate>Sat, 20 Feb 2016 16:24:00 GMT</pubDate></item><item><title>GGX London 2015 - How to create conference Android App with Groovy and WordPress?</title><link>https://sergiodelamo.com/blog/ggx-2015-how-to-create-conference-android-app-with-groovy-and-wordpress.html</link><description><![CDATA[<p>Link to my talk at GGX London 2015 hosted at Skillsmatter.</p><p><a href="https://skillsmatter.com/skillscasts/6919-how-to-create-conference-android-app-with-groovy-and-wordpress"><img src="https://images.sergiodelamo.com/How_to_create_conference_Android_App_with_Groovy_and_Wordpress____SkillsCast___14th_December_2015.png" alt="" /></a></p><p>Talk description</p><blockquote><p>In this talk Sergio will show you how to create conference websites with WordPress custom post types and custom fields, use a Groovy Android library to consume your WordPress’s generated JSON API, and create a simple Android App which shows the conference data. Also, if you would like to create a cheap and easy-to-use backend for your clients, Sergio will show you how to do it in WordPress and consume it easily with Groovy on Android.</p></blockquote>]]></description><guid>ggx-2015-how-to-create-conference-android-app-with-groovy-and-wordpress</guid><pubDate>Mon, 14 Dec 2015 05:07:00 GMT</pubDate></item></channel></rss>