<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: k-mack</title>
    <description>The latest articles on Forem by k-mack (@kmack).</description>
    <link>https://forem.com/kmack</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F48624%2F80f712de-89b2-4b5d-b0e9-a6274bb50f41.jpg</url>
      <title>Forem: k-mack</title>
      <link>https://forem.com/kmack</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kmack"/>
    <language>en</language>
    <item>
      <title>Techniques for Writing Docs in Markup Languages</title>
      <dc:creator>k-mack</dc:creator>
      <pubDate>Fri, 24 Dec 2021 18:18:33 +0000</pubDate>
      <link>https://forem.com/kmack/techniques-for-writing-docs-in-markup-languages-4gke</link>
      <guid>https://forem.com/kmack/techniques-for-writing-docs-in-markup-languages-4gke</guid>
      <description>&lt;p&gt;Software projects need to have good documentation. This makes the software more approachable and impacts its users and contributors. More importantly, it increases the software's signal-to-noise ratio, allowing developers to better understand if the software meets their needs. This is not revolutionary: high-level software developed today (i.e., using the C programming language or above) presumably uses third-party code, open-source or not. Writing software documentation, however, is challenging and thoughtful work. This is the reason why technology companies hire technical writers and developer advocates: they know good documentation is critical for their product.&lt;/p&gt;

&lt;p&gt;Markup languages, such as Markdown and AsciiDoc, have become essential in developing software documentation. Their similarity to programming, with toolchains and a lightweight syntax, aligns with the developer mindset more so than traditional word processors. Documents written in them have a low barrier to read (e.g., you need only a text viewer installed, like &lt;code&gt;more&lt;/code&gt;) and easy to read &lt;code&gt;diff&lt;/code&gt;s, making their history easy to track using a version control system.&lt;/p&gt;

&lt;p&gt;Like software source code, markup languages allow authors to write the same content a thousand different ways. With this type of flexibility and creativity in the documentation process, it helps to employ techniques to make writing documentation as enjoyable as writing code and to make the documentation source files as aesthetically pleasing, consistent, and efficient as well-styled source code. This post shares four such techniques that have improved my writing experience.&lt;/p&gt;

&lt;p&gt;But first, a short overview of my journey with writing technical documentation :).&lt;/p&gt;

&lt;h2&gt;
  
  
  Personal Context
&lt;/h2&gt;

&lt;p&gt;When I joined the company I am at now in 2012, Microsoft Word was primarily used throughout the company for authoring software documentation. It was not used for software library API documentation; tools like Doxygen and Javadoc were used for that. Word was for creating user and administrator guides, tutorials, frequently asked questions (FAQs), etc. The primary reason for using Word was that customers expected documentation as Word documents, and the customers were application users, not developers.&lt;/p&gt;

&lt;p&gt;This was back when Subversion was the dominate version control system in the company, and the slack to invest in tooling around documentation generation and transformation didn't exist. Combining these with some other old-school mentalities meant that managing documentation was a bit painful: The latest and stable version of the document was maintained by one person. If a person was to modify the document, the person had to obtain a copy of the latest version, rename the file such that its name was suffixed with the author's initials (e.g., &lt;code&gt;User_Guide-km.docx&lt;/code&gt;), turn on Word's Track Changes feature, make the modifications, and send the modified file to the maintainer. The maintainer was responsible for merging the edits of all the contributors into the document.&lt;br&gt;
The document was then sent to all contributors for review to ensure no edit was lost. If an error was found in the document during review, then the workflow's recursion kicked in until everyone was satisfied the document.&lt;/p&gt;

&lt;p&gt;This was a bit of gut punched to me. I joined the company after finishing graduate school where I wrote my reports and thesis in LaTeX. I was accustomed to treating documentation like code: use a toolchain to transform source files to an output format, automate document generation (e.g., with GNU Make), and commit changes to version control. My brain was wired to think about content first and its presentation second (e.g., &lt;code&gt;*.cpp -&amp;gt; *.o -&amp;gt; {libmylib.a, libmylib.so}&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;After the company made the move to Git and when Markdown became a hit with the staff, teams started to use markup languages to write software documentation. Contributors were branching and merging edits to Markdown files in Git. Each change to the Markdown files could be easily seen via &lt;code&gt;git diff&lt;/code&gt; and &lt;code&gt;git show&lt;/code&gt;. It was contributor-friendly, and the world seemed right and just :P. The last step was to convert the final, peer-reviewed Markdown content into a Word document. Tools like pandoc were found to be helpful for this. As time went on, things kept getting better: customers became more open to PDF and HTML, teams moved from Markdown to Asciidoctor to create richer documents, and the company-hosted GitLab instance made reviewing changes a delight.&lt;/p&gt;

&lt;p&gt;The world, however, isn't black and white, and as is usual for software developers, technique and style became a topic of discussion. One of the nice things about Word is that it puts all authors into the same frame of mind. The structure of Word is uniform and universal. It matches how we are taught to write from an early age: pull out a blank piece of paper and start writing from left to right, top to bottom. Made a mistake? Erase, use Wite-Out, or start over. There is no commenting out sentences or variable substitution. What you write is what you get, and Word follows this. At least to my knowledge, you can't separate content from presentation in Word: Sentences are delimited by a period and a space. Paragraphs are delimited by a newline. Ordered and unordered lists use a default icon and scheme, set by the person who created the Word document. Looking back, this rigid word processing environment eased the contribution and merging process in the sense that, for example, you didn't have Bob breaking his sections into separate Word documents while Nancy confined her contributions to one document but wrote each sentence on a separate line. Transitioning from Word to markup introduced "developer chaos" in managing documentation contributions. This chaos mirrors that which is experienced with coding best practices and styles.&lt;/p&gt;

&lt;p&gt;After a bit of back in forth about how to manage the flexibility of markup languages, I found some core techniques that helped me write good and maintainable documentation.&lt;/p&gt;
&lt;h2&gt;
  
  
  Techniques for Writing Docs
&lt;/h2&gt;

&lt;p&gt;In 2015, I found and watched &lt;a href="https://github.com/mojavelinux" rel="noopener noreferrer"&gt;Dan Allen&lt;/a&gt;'s presentation at Devnexus titled "&lt;a href="https://www.youtube.com/watch?v=Aq2USmIItrs" rel="noopener noreferrer"&gt;Discover the Zen of Writing with Asciidoctor&lt;/a&gt;." The full presentation is great whether you use Asciidoctor or not. However, the gold for authors is his section on &lt;a href="https://www.youtube.com/watch?v=Aq2USmIItrs&amp;amp;t=3454s" rel="noopener noreferrer"&gt;Zen techniques&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full disclosure: The techniques below are from Dan Allen's 2015 Devnexus presentation (see links above). Full credit goes to him. I am merely echoing them in an attempt to share them and attest to how awesome they are.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After giving credit to where credit is due, let's dive into some of the techniques that Dan shares in his presentation. Note that this post could be titled something like "4 Writing Techniques Every Programmer Should Know," but it sounds like clickbait to me. Then again, I am just generally not good with creating titles.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Like Code, Don't Repeat Yourself
&lt;/h3&gt;

&lt;p&gt;Since writing technical documents in a markup language is like writing code, don’t repeat yourself. Just like copy-and-pasting code throughout an application can result in inconsistencies, duplicating the same text throughout your documents can leave them fragmented when one section is modified but the others are not.&lt;/p&gt;

&lt;p&gt;There are tools that allow authors to import documents into other documents. This makes it easy to pull out licenses, introductions, appendices, images, etc. into their own documents and then import them into others. Changes in these then fan out to all the other documents.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Like Code, Write a Statement Per Line
&lt;/h3&gt;

&lt;p&gt;Most statements in code are given their own line. Apply this same principle to your documents: every sentence or significant clause (or semi-colon, colon, or list) starts a new line. Most documentation formats require a blank line to introduce a paragraph. Therefore, contiguous statements per line will be rendered as a single paragraph. This is a powerful writing technique for a number of reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It taps into your programming mindset and aligns with the technical, structured workflow that you know and use everywhere else.&lt;/li&gt;
&lt;li&gt;If a change occurs, it is localized to that line (i.e., no wrapping) which means the diff is incredibly easy to read.&lt;/li&gt;
&lt;li&gt;Long sentences run over 80 columns, which means you’re probably ranting or meandering.
Technical documents are not novels; they should be concise and to the point.
Of course, some sentences are going to be over 80 characters, but if you’re pushing 100 characters, consider revising.
In other words, be judicious.
(A &lt;a href="https://www.powerthesaurus.org/" rel="noopener noreferrer"&gt;thesaurus&lt;/a&gt; can help you say more with less.)&lt;/li&gt;
&lt;li&gt;Sentences can be easily moved around.&lt;/li&gt;
&lt;li&gt;Sentences can be easily commented out (if your documentation syntax supports comments).&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  3. Like Code, Write Comments
&lt;/h3&gt;

&lt;p&gt;Commenting is powerful in code and just as powerful in documents. Comments in your document allow you to save off chunks of text without being rendered in your generated document. This is important for keeping around thoughts and objectives for paragraphs, sections, etc. This helps the other authors and editors understand what you are trying to accomplish.&lt;/p&gt;

&lt;p&gt;Not all markup formats support comments. If the one you use supports them, then don’t be afraid to use them.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Like Code, Use Variables
&lt;/h3&gt;

&lt;p&gt;A number of documentation formats support variables that can encapsulate information used repeatedly throughout a document. This is also a way to shorten sentences to fit on a single line and to keep from repeating yourself. I like to use Asciidoctor's attributes to store the long and abbreviated names of the application I am writing about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// attributes
:app-abbr: App
:app-name: Long Application Name Here
:app-uri-downloads: https://sourceforge.net/
:app-uri-downloads-link: {app-uri-downloads}[Downloads]

== Introduction

{app-name} ({app-abbr}) is an application that *blah blah blah*.

// ...

== Getting Started

Get started with {app-abbr} by downloading a pre-built binary from {app-uri-downloads-link}.

// ...

== FAQs

[quote]
Where can I download {app-abbr}?

Pre-built binaries can be downloaded from {app-uri-downloads-link}.

// ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The goal of this post was to share techniques to help make writing good software documentation enjoyable with a markup language. The four techniques shared were credited to Dan Allen of the Asciidoctor project.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fin&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>writing</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Groovy's @CompileStatic and Methods with the Same Name</title>
      <dc:creator>k-mack</dc:creator>
      <pubDate>Sat, 06 Nov 2021 23:22:29 +0000</pubDate>
      <link>https://forem.com/kmack/groovys-compilestatic-and-methods-with-the-same-name-2a24</link>
      <guid>https://forem.com/kmack/groovys-compilestatic-and-methods-with-the-same-name-2a24</guid>
      <description>&lt;p&gt;Application programming interfaces (APIs) can get whacky, but compiled languages help users to get things semantically correct. And dynamic languages? Their ergonomic "dynamic sauce" ladled over a codebase can sometimes be less than helpful. This post is about how Groovy's &lt;code&gt;@CompileStatic&lt;/code&gt; can help to demystify what is happening at the call site of a method that has the same name and descriptor as another.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part I: The Java API
&lt;/h2&gt;

&lt;p&gt;Let's start with what we know we cannot do in Java: No two methods in one class file may have the same name and descriptor.&lt;sup id="fnref1"&gt;1&lt;/sup&gt; The Java compiler simply does not let us do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;something&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;something&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the above class is compiled, an error message complaining that method &lt;code&gt;static void something()&lt;/code&gt; is already defined.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"public class MyClass{&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; public String something(){}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; public static String something(){}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/MyClass.java          
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JDK11_HOME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/bin/javac /tmp/MyClass.java
&lt;span class="go"&gt;/tmp/MyClass.java:3: error: method something() is already defined in class MyClass
  public static String something() {}
                     ^
1 error
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Java 8 updated the language to permit interfaces to have static methods, so we can update our API such that it has a concrete class with an instance and a static method that use the same name and descriptor. I would not say this is a common thing to see in an API, but I have seen it. In fact, this post is based on my experience with using one :).&lt;/p&gt;

&lt;p&gt;Let's refactor the above class to use interfaces to give the impression that it has two methods with the same name and descriptor. One interface will define &lt;code&gt;String something()&lt;/code&gt;; another interface will define &lt;code&gt;static String something()&lt;/code&gt;; and a concrete class will implement the two interfaces. The concrete class will compile because it actually no longer has &lt;code&gt;static String something()&lt;/code&gt; as part of its implementation. When calling the static method, it must be through the interface, not the concrete class, because &lt;em&gt;the static method is part of the interface&lt;/em&gt;. The code for these three files is below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// InterfaceWithSomething.java&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;InterfaceWithSomething&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;something&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// InterfaceWithStaticSomething.java&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;InterfaceWithStaticSomething&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;something&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Interface's static method"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Implementation.java&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Implementation&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;InterfaceWithSomething&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;InterfaceWithStaticSomething&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@Override&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;something&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Implementation's instance method"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will use the above classes as our Java API. Let's get groovy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part II: The Groovy App
&lt;/h2&gt;

&lt;p&gt;Since the JVM is our development platform, we can mix JVM languages. Let's use our Java API in some Groovy code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="c1"&gt;// GroovyCode.groovy&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GroovyCode&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;impl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Implementation&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt; &lt;span class="n"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;something&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;groovy -cp . GroovyCode.groovy 
Interface's static method
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cool! Wait, what? We are creating a new &lt;em&gt;instance&lt;/em&gt; of &lt;code&gt;Implementation&lt;/code&gt; and invoking the &lt;em&gt;instance&lt;/em&gt; method &lt;code&gt;String something()&lt;/code&gt;. Why is Groovy invoking the &lt;em&gt;static&lt;/em&gt; method with the same name, especially since Java does not permit us to invoke &lt;code&gt;Implementation.something()&lt;/code&gt; as that method is only part of &lt;code&gt;InterfaceWithStaticSomething&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I actually do not know the answer to this question!&lt;/strong&gt; What I do know is that there must be ambiguity at the call site, where or what, again, I do not know. The Groovy runtime decides to invoke &lt;code&gt;InterfaceWithStaticSomething.something()&lt;/code&gt;. If we use the Groovy Console to inspect the AST and view the bytecode generated from &lt;code&gt;GroovyCode&lt;/code&gt;, we see this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static varargs main([Ljava/lang/String;)V
 L0
  INVOKESTATIC GroovyCode.$getCallSiteArray ()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
  ASTORE 1
 L1
  LINENUMBER 4 L1
  ALOAD 1
  LDC 0
  AALOAD
  LDC LImplementation;.class
  INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;)Ljava/lang/Object; (itf)
  LDC LImplementation;.class
  INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
  CHECKCAST Implementation
  ASTORE 2
 L2
  ALOAD 2
  POP
 L3
  LINENUMBER 5 L3
  ALOAD 1
  LDC 1
  AALOAD
  LDC LGroovyCode;.class
  ALOAD 1
  LDC 2
  AALOAD
  ALOAD 2
  INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.call (Ljava/lang/Object;)Ljava/lang/Object; (itf)
  INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callStatic (Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/Object; (itf)
  POP
 L4
  LINENUMBER 6 L4
  RETURN
  LOCALVARIABLE args [Ljava/lang/String; L0 L4 0
  LOCALVARIABLE impl LImplementation; L2 L4 2
  MAXSTACK = 4
  MAXLOCALS = 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Near the bottom of &lt;code&gt;L3&lt;/code&gt; we see the &lt;a href="https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html#jvms-6.5.invokeinterface" rel="noopener noreferrer"&gt;&lt;code&gt;INVOKEINTERFACE&lt;/code&gt;&lt;/a&gt; instruction being used twice. At both occurrences, it is calling an interface method on &lt;code&gt;CallSite&lt;/code&gt;, which is implemented with Groovy meta code. These two instructions dynamically invoke &lt;code&gt;String something()&lt;/code&gt; (on the &lt;code&gt;Implementation&lt;/code&gt; object) and &lt;code&gt;println&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ultimately, whatever the ambiguity is that is causing &lt;code&gt;CallSite&lt;/code&gt; to select the static method over the instance method, we need to remove it. Instead of relying on the "dynamic sauce" of Groovy's runtime, we need to break through it. We need the rigid static compilation that Java gives us. We need &lt;code&gt;@CompileStatic&lt;/code&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Part III: @CompileStatic
&lt;/h2&gt;

&lt;p&gt;Here is what Groovy's documentation says about &lt;code&gt;@CompileStatic&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This will let the Groovy compiler use compile time checks in the style of Java then perform static compilation, thus bypassing the Groovy meta object protocol.&lt;/p&gt;

&lt;p&gt;When a class is annotated, all methods, properties, files, inner classes, etc. of the annotated class will be type checked. When a method is annotated, static compilation applies only to items (closures and anonymous inner clsses &lt;em&gt;[sic]&lt;/em&gt;) within the method.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source: &lt;a href="https://docs.groovy-lang.org/2.4.2/html/gapi/groovy/transform/CompileStatic.html" rel="noopener noreferrer"&gt;https://docs.groovy-lang.org/2.4.2/html/gapi/groovy/transform/CompileStatic.html&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is exactly what we need, and the really nice thing is that we only need to annotate the method &lt;code&gt;main(String[])&lt;/code&gt;. Let's do that and see what happens.&lt;/p&gt;

&lt;p&gt;First, we annotate what we want to be statically compiled, which is &lt;code&gt;GroovyCode&lt;/code&gt;'s &lt;code&gt;main(String[])&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GroovyCode&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@groovy.transform.CompileStatic&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Implementation&lt;/span&gt; &lt;span class="n"&gt;impl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Implementation&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt; &lt;span class="n"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;something&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let's run the Groovy code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;groovy -cp . GroovyCode.groovy 
Implementation's static method
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Excellent. We are now calling the method of our Java API that we originally set out to call.&lt;/p&gt;

&lt;p&gt;Lastly, let's look at the bytecode using the Groovy Console again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static varargs main([Ljava/lang/String;)V
 L0
  LINENUMBER 4 L0
  NEW Implementation
  DUP
  INVOKESPECIAL Implementation.&amp;lt;init&amp;gt; ()V
  ASTORE 1
 L1
  ALOAD 1
  POP
 L2
  LINENUMBER 5 L2
  LDC LGroovyCode;.class
  ALOAD 1
  INVOKEVIRTUAL Implementation.something ()Ljava/lang/String;
  INVOKESTATIC org/codehaus/groovy/runtime/DefaultGroovyMethods.println (Ljava/lang/Object;Ljava/lang/Object;)V
  ACONST_NULL
  POP
 L3
  LINENUMBER 6 L3
  RETURN
  LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
  LOCALVARIABLE impl LImplementation; L1 L3 1
  MAXSTACK = 2
  MAXLOCALS = 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see there is less bytecode generated. This makes sense as static compilation removes the &lt;a href="https://groovy-lang.org/metaprogramming.html#_runtime_metaprogramming" rel="noopener noreferrer"&gt;runtime metaprogramming&lt;/a&gt;. Also, we can see that the two &lt;code&gt;INVOKEINTERFACE&lt;/code&gt; instructions we noticed before have been replaced. The first is now &lt;code&gt;INVOKEVIRTUAL&lt;/code&gt; and invokes &lt;code&gt;Implementation.something()&lt;/code&gt; -- the API call we have been after this whole time. The second is now &lt;code&gt;INVOKESTATIC&lt;/code&gt; and invokes the &lt;code&gt;println&lt;/code&gt; method. Both of which are more efficient that hopping through the call site metaprogramming that was there before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Sometimes you need to cut through dynamic invocation magic to ensure what you intend to happen actually happens. The example discussed in this post was attempting to invoke an instance method that had the same name as a static method provided by a Java Interface. Groovy's runtime metaprogramming invoked the static method instead of the instance method, even though the call site looked unambiguous. We corrected the behavior of the Groovy code by using the &lt;code&gt;@CompileStatic&lt;/code&gt; annotation to statically compile the call site.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fin&lt;/em&gt;.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.6" rel="noopener noreferrer"&gt;https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.6&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>groovy</category>
      <category>java</category>
      <category>metaprogramming</category>
    </item>
    <item>
      <title>HTTPS Client Certificate Authentication With Java</title>
      <dc:creator>k-mack</dc:creator>
      <pubDate>Sun, 07 Mar 2021 23:26:45 +0000</pubDate>
      <link>https://forem.com/kmack/https-client-certificate-authentication-with-java-l78</link>
      <guid>https://forem.com/kmack/https-client-certificate-authentication-with-java-l78</guid>
      <description>&lt;p&gt;I recently had to develop a Java client to interface with an internal service over HTTPS that required client certificate authentication. It is not often that I need to dive into SSL certificates, and doing so usually requires me to step back and relearn some things. This situation was no different, but in an attempt to burn this stuff into my brain, I am writing about it here.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's a Trust Thing
&lt;/h2&gt;

&lt;p&gt;First thing's first: the client needs to trust the HTTPS connection that the service wants to establish. The server certificate used by the service is signed by an internal certificate authority (CA). Since the client code runs on the Java Virtual Machine (JVM), it is by default subject to the collection of trusted CA certificate chains (Chain of Trust) used the JVM, which -- and rightly so -- does not include the CA that signed the service's server certificate.&lt;/p&gt;

&lt;p&gt;The JVM's Chain of Trust is contained inside the file &lt;code&gt;cacerts&lt;/code&gt; located in the Java Development Kit's (JDK) security directory. You can use &lt;code&gt;keytool&lt;/code&gt;, which comes with the Java Development Kit (JDK), if you are curious about which CAs are trusted by your JVM. In fact, &lt;code&gt;keytool&lt;/code&gt; has the command-line option &lt;code&gt;-cacerts&lt;/code&gt; to short-circuit specifying the JDK's &lt;code&gt;cacerts&lt;/code&gt; as the command's keystore. For example, if you wanted to know if your JDK trusts certificates generated by Let's Encrypt, you would want to know that it trusts IdenTrust’s "DST Root CA X3":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Files\Java\zulu11.41.23-jdk11.0.8&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\bin\keytool.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-cacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IdenTrust"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;password:&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;identrustcommercial&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;jdk&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Jan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;2014&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;trustedCertEntry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;identrustdstx3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;jdk&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Sep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;trustedCertEntry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c"&gt;# &amp;lt;-- Bingo!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;identrustpublicca&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;jdk&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Jan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;2014&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;trustedCertEntry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Client code using the default JVM Chain of Trust will not allow a secure connection to be established with the service. The service's server certificate, inspected by the client's JVM during the SSL/TLS handshake, is seen as untrustworthy since it is signed by a CA not found in &lt;code&gt;cacerts&lt;/code&gt;. This handshaking and rejection of trust is taken care of by the JVM networking and security code running beneath the client code.&lt;/p&gt;

&lt;p&gt;As a quick demonstration, the following (&lt;a href="https://spockframework.org/" rel="noopener noreferrer"&gt;Spock&lt;/a&gt;) test asserts that the client JVM code fails to create an SSL connection with the service. Note that I chose to use &lt;a href="https://vertx.io/docs/vertx-web-client/java/" rel="noopener noreferrer"&gt;Vert.x Web Client&lt;/a&gt; to handle interacting with the service, but don't let this decision distract from the core content of this post. Nevertheless, if you haven't used Vert.x, I encourage you to try it out -- especially for building server-side network applications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClientTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Specification&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Vertx&lt;/span&gt; &lt;span class="n"&gt;vertx&lt;/span&gt;
  &lt;span class="n"&gt;WebClientOptions&lt;/span&gt; &lt;span class="n"&gt;webClientOptions&lt;/span&gt;
  &lt;span class="n"&gt;WebClient&lt;/span&gt; &lt;span class="n"&gt;webClient&lt;/span&gt;
  &lt;span class="n"&gt;AsyncConditions&lt;/span&gt; &lt;span class="n"&gt;conditions&lt;/span&gt;

  &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;vertx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Vertx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;vertx&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;webClientOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;WebClientOptions&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setSsl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;conditions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;AsyncConditions&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="s2"&gt;"cannot establish secure connection with an untrusted CA"&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;given:&lt;/span&gt;
    &lt;span class="n"&gt;webClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vertx&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;webClientOptions&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

    &lt;span class="nl"&gt;expect:&lt;/span&gt;
    &lt;span class="n"&gt;webClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"service.cn.local"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/api/endpoint"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;conditions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;evaluate&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;SSLHandshakeException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cause&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="s2"&gt;"Failed to create SSL connection"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cause&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;})&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cleanup&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;webClient&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;vertx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The solution is straight-forward: we need to tell the JVM that it can trust the service's server certificate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trust Me, I Got This
&lt;/h2&gt;

&lt;p&gt;There are a couple of ways to have the JVM trust that the connection attempted to be made by the service really is secure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the CA certificate chain to the JVM's &lt;code&gt;cacerts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a separate keystore and use that in the client code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Although the first option provides a quick solution to the problem, it will effectively allow any future code that runs on the JVM with the modified &lt;code&gt;cacerts&lt;/code&gt; to trust certificates signed by the internal CA. This may be perfectly acceptable given the CA;&lt;br&gt;
e.g., if it's the root CA of your organization, then it probably makes sense to include it in your JVM's default Chain of Trust. In my case, the internal CA is for a dedicated and isolated system,&lt;br&gt;
and I actually do not want to create an environment where any Java application running on the JVM can interface with the internal service. I only want the one application that I am developing to have this capability. Additionally, the client code does not need to interface with any other system over a secure connection, so I do not need to trust any SSL certificates besides those that are signed by the internal CA. Therefore, the second option suits me better.&lt;/p&gt;
&lt;h3&gt;
  
  
  Create a Java Keystore
&lt;/h3&gt;

&lt;p&gt;The good people at IT sent me the internal CA certificate chain and a client certificate for authenticating with the service. The CA certificate chain was a PFX file and the client certificate was a P12 file. I installed both on my Windows machine to enable my web browsers to trust and authenticate with the internal service.&lt;/p&gt;

&lt;p&gt;From this setup, a separate Java keystore containing the internal CA certificate chain can be created. First, export the root CA certificate and its intermediate certificates. These can be found on a Windows machine at &lt;code&gt;Control Panel &amp;gt; Internet Options &amp;gt; Content (Tab) &amp;gt; Certificates (Button)&lt;/code&gt;. From there,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;find and export the trusted root CA as a DER encoded binary X.509 (CER), and&lt;/li&gt;
&lt;li&gt;find and export each intermediate certificates as a DER encoded binary X.509 (CER).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use &lt;code&gt;keytool&lt;/code&gt; to create a new keystore file that trusts the exported certificates (add the root CA first):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Files\Java\zulu11.41.23-jdk11.0.8&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\bin\keytool.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-importcert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;internal-ca-root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Temp\internal-ca-root.cer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--storetype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JKS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Temp\internal.jks&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;password:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Re-enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;password:&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Trust&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;certificate&lt;/span&gt;&lt;span class="nf"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;yes&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Certificate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;was&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;added&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Files\Java\zulu11.41.23-jdk11.0.8&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\bin\keytool.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-importcert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;internal-ca-intermediate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Temp\internal-ca-intermediate.cer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--storetype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;JKS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Temp\internal.jks&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;password:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Re-enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;password:&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Trust&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;certificate&lt;/span&gt;&lt;span class="nf"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;yes&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Certificate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;was&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;added&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the first command creates the keystore, the password you enter when prompted will set the password for it. When prompted for the password after entering the second command, the same password must be used.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure Client to Use Keystore
&lt;/h3&gt;

&lt;p&gt;The created Java keystore containing the Chain of Trust can now be used in the client code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClientTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Specification&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="s2"&gt;"can establish secure connection with separate keystore"&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;given:&lt;/span&gt;
    &lt;span class="n"&gt;webClientOptions&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTrustStoreOptions&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;JksOptions&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CUSTOM_KEYSTORE_PATH&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CUSTOM_KEYSTORE_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;webClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vertx&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;webClientOptions&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

    &lt;span class="nl"&gt;when:&lt;/span&gt;
    &lt;span class="n"&gt;webClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"service.cn.local"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/api/endpoint"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;conditions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;evaluate&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;succeeded&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;CONNECTED_BUT_NOT_AUTHENTICATED&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;bodyAsString&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;})&lt;/span&gt;

    &lt;span class="nl"&gt;then:&lt;/span&gt;
    &lt;span class="n"&gt;conditions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;await&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although a secure connection is now established between the client and service, the two-way SSL authentication is not yet satisfied. Hence, the service responds with a message indicating the client cannot reach the desired endpoint because it was not authenticated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Certificate, Please
&lt;/h2&gt;

&lt;p&gt;As mentioned at the beginning of the post&lt;br&gt;
-- and the original motivation for writing this --&lt;br&gt;
the internal web service uses two-way SSL authentication. This means a client is authenticated when it presents a client certificate to the service that is issued by the root CA. If a client certificate is not provided (like in the test above) or it is not signed by the root CA, then the service denies the client's request.&lt;/p&gt;

&lt;p&gt;The final step, therefore, is to configure the client code to authenticate with the server using the client certificate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClientTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Specification&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="s2"&gt;"can establish secure connection and authenticate with client certificate"&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;given:&lt;/span&gt;
    &lt;span class="n"&gt;webClientOptions&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTrustStoreOptions&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;JksOptions&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CUSTOM_KEYSTORE_PATH&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CUSTOM_KEYSTORE_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPfxKeyCertOptions&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;PfxOptions&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CLIENT_CERT_PATH&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CLIENT_CERT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;webClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vertx&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;webClientOptions&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

    &lt;span class="nl"&gt;when:&lt;/span&gt;
    &lt;span class="n"&gt;webClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"service.cn.local"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/api/endpoint"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;conditions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;evaluate&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;succeeded&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;CONNECTED_AND_AUTHENTICATED&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;bodyAsString&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;})&lt;/span&gt;

    &lt;span class="nl"&gt;then:&lt;/span&gt;
    &lt;span class="n"&gt;conditions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;await&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The client-server connection is secured over HTTPS, and the service authenticates the client. Note that Vert.x Web Client makes this easy, but I assume it is just as easy with other HTTP client libraries that support HTTPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Sometimes you need to establish secure connections with servers that have not been signed by a globally trusted entity. In these situations, it is can be desirable to create a custom keystore containing the Chain of Trust associated with the server(s) you need to interface with. Java's &lt;code&gt;keytool&lt;/code&gt; can be used to do this.&lt;br&gt;
If a server employs two-way SSL authentication, you can use an HTTP library, like Vert.x Web Client, to add a client certificate to your HTTPS connection.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fin&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>https</category>
      <category>ssl</category>
      <category>vertx</category>
    </item>
  </channel>
</rss>
