
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
 <channel>
   <title>Relentless Coding</title>
   <link>https://relentlesscoding.com/</link>
   <description>Recent content on Relentless Coding</description>
   <generator>Hugo -- gohugo.io</generator>
   <language>en-us</language>
   <copyright>Copyright &amp;copy; Stefan van den Akker 2024</copyright>
   <lastBuildDate>Sun, 10 Aug 2025 09:36:18 +0200</lastBuildDate>
   
       <atom:link href="https://relentlesscoding.com/index.xml" rel="self" type="application/rss+xml" />
   
   
     <item>
       <title>Kagi Assistant Times Out when on VPN</title>
       <link>https://relentlesscoding.com/posts/kagi-vpn-model-timeouts/</link>
       <pubDate>Sun, 10 Aug 2025 09:36:18 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/kagi-vpn-model-timeouts/</guid>
       <description>&lt;p&gt;I use &lt;a href=&#34;https://kagi.com&#34;&gt;Kagi&lt;/a&gt; as my search engine and for LLM interactions. I am also alwaysconnected to ProtonVPN. I and others have noticed that the output from their&lt;a href=&#34;https://kagi.com/assistant&#34;&gt;LLM Assistant&lt;/a&gt; is cut of after a couple of seconds &lt;em&gt;when onProtonVPN&lt;/em&gt;. Why does that happen and what can we do about it?&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;whats-wrong-with-vpns&#34;&gt;What&amp;rsquo;s Wrong with VPNs?&lt;/h2&gt;&lt;p&gt;Instead of a typical LLM response flow, we see:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/kagi-vpn-model-timeouts/error1.webp&#34; alt=&#34;Kagi Assistant shows error saying &amp;ldquo;Something went wrong while trying to getyou your response&amp;rdquo;&#34; title=&#34;Kagi Assistantshows error saying &amp;quot;Something went wrong while trying to get you yourresponse&amp;quot;&#34;&gt;&lt;/p&gt;&lt;p&gt;At first, the problem seemed to be related to being on a VPN:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;vpn$ H=kagi.com; time openssl s_client -quiet -servername $H $H:443 Connecting to 34.111.242.115depth=2 C=US, O=Google Trust Services LLC, CN=GTS Root R1verify return:1depth=1 C=US, O=Google Trust Services, CN=WR3verify return:1depth=0 CN=kagi.comverify return:180CB5B021D740000:error:0A000126:SSL routines::unexpected eof whilereading:ssl/record/rec_layer_s3.c:696:real    0m6.599suser    0m0.005ssys 0m0.012s&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When using the LLM from a static residential IP, no timeout occurs (I gotimpatient, so terminated with &lt;code&gt;^C&lt;/code&gt;):&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;residential$ H=kagi.com; time openssl s_client -quiet -servername $H $H:443Connecting to 34.111.242.115depth=2 C=US, O=Google Trust Services LLC, CN=GTS Root R1verify return:1depth=1 C=US, O=Google Trust Services, CN=WR3verify return:1depth=0 CN=kagi.comverify return:1^Creal    1m25.933suser    0m0.016ssys 0m0.007s&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At first, I thought that maybe ProtonVPN ages out TCP connections after a coupleof seconds of inactivity to free space in its NAT/PAT translation table. Butthat seemed like a draconian expiration, and also, it didn&amp;rsquo;t bear out:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;vpn$ H=nos.nl; time openssl s_client -quiet -servername $H $H:443 Connecting to 18.239.50.102depth=2 C=GB, O=Sectigo Limited, CN=Sectigo Public Server Authentication RootR46verify return:1depth=1 C=GB, O=Sectigo Limited, CN=Sectigo Public Server Authentication CA EVR36verify return:1depth=0 serialNumber=32142157, jurisdictionC=NL, businessCategory=PrivateOrganization, C=NL, ST=Noord-Holland, O=Nederlandse Omroep Stichting, CN=nos.nlverify return:1^Creal    0m32.315suser    0m0.007ssys 0m0.009s&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;oh-so-its-google&#34;&gt;Oh, So It&amp;rsquo;s Google&lt;/h2&gt;&lt;p&gt;OpenAI&amp;rsquo;s o3 model suggested to me that it might be Google&amp;rsquo;s Load Balancer. Yousee, every ProtonVPN server has a pool of addresses. Some or all of theseaddresses are marked by Google as high-risk, because a lot of simultaneousconnections and/or attacks originate from them to Google properties. To preventcertain kind of attacks (&lt;a href=&#34;https://en.wikipedia.org/wiki/Slowloris_(cyber_attack)&#34;&gt;Slowloris&lt;/a&gt;), resource depletion), Google tears downconnections when the client is not sending data. Looking at a typical exchangewith Wireshark, you will notice that if the model thinks more than 5 seconds,the connection will be torn down. If the model starts outputting its responsebefore 5 seconds have elapsed, the connection will still be cut down exactly 5seconds after the client sent its last TCP segment with a payload (i.e. theprompt). Although Wireshark shows my client sending many subsequent ACKs, noneof these carry a payload, and they are thus not seen by Google as meaningfultraffic:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/kagi-vpn-model-timeouts/wireshark.webp&#34; alt=&#34;Wireshark shows a lot of empty TCPACKs&#34; title=&#34;Wireshark shows a lot ofempty TCP ACKs&#34;&gt;&lt;/p&gt;&lt;p&gt;I haven&amp;rsquo;t found any documentation to back up these claims, but the empiricaldata is there.&lt;/p&gt;&lt;h2 id=&#34;what-can-you-do&#34;&gt;What Can You Do?&lt;/h2&gt;&lt;p&gt;A workaround would be to try another egress IP address by switching to adifferent (Proton)VPN server. Anecdotally, this seems to work, which suggeststhat Google has not put the entire ProtonVPN IP range on its naughty list.&lt;/p&gt;&lt;p&gt;Seeing that Kagi is using Google infrastructure, they might also be able to dosomething about this by increasing this 5s teardown rule for high-risk IPaddresses in their Google console. I do not know whether that is (1) possible or(2) desirable, though, as it might open them up to potential DoS.&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai spell: --&gt;</description>
     </item>
   
     <item>
       <title>Choose Which Cisco OS to Boot</title>
       <link>https://relentlesscoding.com/posts/choose-cisco-os-at-startup/</link>
       <pubDate>Mon, 26 May 2025 12:25:00 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/choose-cisco-os-at-startup/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s have a look at what Cisco OS gets booted at startup and how we caninfluence that deciscion using the configuration register.&lt;/p&gt;&lt;!-- more --&gt;&lt;p&gt;The configuration register is a 2-byte field, represented as 4 hex digits, thatyou can set with the &lt;code&gt;config-register&lt;/code&gt; global configuration command.&lt;/p&gt;&lt;p&gt;The last nibble of the register is called the &lt;strong&gt;boot field&lt;/strong&gt;. This is the logicit uses:&lt;/p&gt;&lt;blockquote&gt;&lt;ol&gt;&lt;li&gt;If boot field = 0, use the ROMMON OS.&lt;/li&gt;&lt;li&gt;If boot field = 1, load the first IOS file found in flash memory.&lt;/li&gt;&lt;li&gt;If boot field = 2-F:&lt;ul&gt;&lt;li&gt;Try each boot system command in the startup-config file, in order, untilone works.&lt;/li&gt;&lt;li&gt;If none of the boot system commands work, load the first IOS file foundin flash memory.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;If all other attempts fail, load ROMMON, from which you can perform furthersteps to recover by copying a new IOS image into flash.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&amp;ndash; &lt;cite&gt;ICND1 Chapter 35&lt;/cite&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The process visually (image taken from the same source as the quote):&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/choosing-cisco-os-at-boottime.webp&#34; alt=&#34;Choosing an OS to load at boottime&#34;&gt;&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;</description>
     </item>
   
     <item>
       <title>Quality of Service (QoS)</title>
       <link>https://relentlesscoding.com/posts/qos/</link>
       <pubDate>Sat, 24 May 2025 11:14:05 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/qos/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s have a look at QoS or how to unfairly manage traffic in the face ofcongestion on Cisco devices.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;what-is-qos-and-why-do-we-need-it&#34;&gt;What Is QoS and Why Do We Need It?&lt;/h2&gt;&lt;p&gt;QoS is concerned with 4 characteristics of network traffic:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;bandwidth (capacity of a link)&lt;/li&gt;&lt;li&gt;delay (between sending a packet and the recipient receiving it, or RTT)&lt;/li&gt;&lt;li&gt;variation in delay (jitter)&lt;/li&gt;&lt;li&gt;packet loss&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;QoS comes into play when the network is not able to process every packet as soonas it received, which is most always. Think of a gigabit data stream coming inon 1 interface and having to leave a 100 Mbps link on the other end (speedmismatch). Or several gigabit links on a switch receiving traffic at capacityall having to use the single gigabit uplink to the ROAS (aggregation point).&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/do-you-need-qos.webp&#34; alt=&#34;A speed mismatch on a router with a GigabitEthernet link on the left and aFastEthernet link on the right. Below that, a switch is depicted as theaggregation point of several gigabit links on the left all needing to egress asingle gigabit link on the right.&#34;&gt;&lt;/p&gt;&lt;p&gt;QoS is good for when we have periodic congestion. If we are congested all thetime, we simple need more bandwidth.&lt;/p&gt;&lt;p&gt;Some types of traffic (voice, interactive video) are much more sensitive totiming (delay, jitter) and loss than others (web browsing). If we do not haveany QoS, then all the traffic gets the default, best-effort treatment. Thiscomes down to FIFO, where the first packet that arrives is the first packet thatis scheduled to leave the outgoing interface. Heavy downloading can delay oreven drown out voice traffic, resulting in a poor Quality of Experience (QoE)for the end user on a VoIP call.&lt;/p&gt;&lt;p&gt;We have 4 tools to change the QoS characteristics of certain flows in thenetwork:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;classification and marking&lt;/li&gt;&lt;li&gt;prioritization through queueing&lt;/li&gt;&lt;li&gt;policing (dropping&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;) and shaping (delaying through queuing)&lt;/li&gt;&lt;li&gt;congestion avoidance (random drops or ECN)&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;classification-and-marking&#34;&gt;Classification and Marking&lt;/h2&gt;&lt;p&gt;Cisco recommends to classify and mark traffic as close to the source aspossible. Marking traffic on access devices prevents having to classify trafficover and over again on distribution or core devices (which are really meant todo bulk forwarding, not classification). We might also not be willing to trustmarking from end devices, but overwrite any received markings in trusted accessdevices under control by IT (the trust boundary lies in the access switch).&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/classification-for-queuing-in-a-router.webp&#34; alt=&#34;Classification for queuing in arouter&#34;&gt;&lt;/p&gt;&lt;p&gt;Classification can happen at the datalink layer or the network layer.&lt;/p&gt;&lt;h3 id=&#34;datalinklayer-2-marking&#34;&gt;Datalink/Layer-2 Marking&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/cos.webp&#34; alt=&#34;Class of Service value inside a 802.1q frame&#34; title=&#34;Class of Serviceinside 802.1q frame&#34;&gt;&lt;/p&gt;&lt;p&gt;Inside an IEEE 802.1q trunking frame, there are 3 bits that can be used to set aClass of Service value: priority bits (also known as Priority Code Point [PCP]).In theory, we could set 8 different priority values. But Cisco says we are notallowed to use the highest 2 values, value 6 and 7, because they are reservedfor network use. So, effectively, we can set any value from 0 through 5, with 0being the default (no priority).&lt;/p&gt;&lt;p&gt;Even if we are not using trunking on an interface, we might still be able to use&lt;a href=&#34;https://en.wikipedia.org/wiki/IEEE_P802.1p&#34;&gt;IEEE 802.1p&lt;/a&gt;. That is the same as 802.1q except that the VLAN ID (VID,the last 12 bits of the 4 bytes) is set to all zeroes.&lt;/p&gt;&lt;p&gt;The &lt;a href=&#34;https://en.wikipedia.org/wiki/IEEE_P802.1p&#34;&gt;IEEE has made some broad recommendations&lt;/a&gt; on how to set andinterpret these CoS values:&lt;/p&gt;&lt;table&gt;  &lt;thead&gt;      &lt;tr&gt;          &lt;th style=&#34;text-align: center&#34;&gt;PCP value&lt;/th&gt;          &lt;th style=&#34;text-align: center&#34;&gt;Priority&lt;/th&gt;          &lt;th style=&#34;text-align: center&#34;&gt;Acronym&lt;/th&gt;          &lt;th style=&#34;text-align: left&#34;&gt;Traffic types&lt;/th&gt;      &lt;/tr&gt;  &lt;/thead&gt;  &lt;tbody&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: center&#34;&gt;1&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;0 (lowest)&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;BK&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;Background&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: center&#34;&gt;0&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;1 (default)&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;BE&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;Best effort&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: center&#34;&gt;2&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;2&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;EE&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;Excellent effort&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: center&#34;&gt;3&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;3&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;CA&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;Critical applications&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: center&#34;&gt;4&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;4&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;VI&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;Video, &amp;lt; 100 ms latency and jitter&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: center&#34;&gt;5&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;5&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;VO&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;Voice, &amp;lt; 10 ms latency and jitter&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: center&#34;&gt;6&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;6&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;IC&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;Internetwork control&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: center&#34;&gt;7&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;7 (highest)&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;NC&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;Network control&lt;/td&gt;      &lt;/tr&gt;  &lt;/tbody&gt;&lt;/table&gt;&lt;h3 id=&#34;network-layerlayer-3-marking&#34;&gt;Network-Layer/Layer-3 Marking&lt;/h3&gt;&lt;p&gt;The problem with layer-2 CoS is that the markings do not cross routerboundaries. We need something similar in IP and we have it in both IPv4 andIPv6.&lt;/p&gt;&lt;p&gt;The 2nd byte of the IPv4 header is called the Type of Service (ToS) orDifferentiated Services. The 2nd and 3rd nibble of the IPv6 header is called&amp;ldquo;Traffic class&amp;rdquo;. The function is the same: the first 6 bits are used to indicatethe Differentiated Services Code Point (DSCP) and the last 2 bits are used forExplicit Congestion Notification (ECN).&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/ipv4-header.webp&#34; alt=&#34;IPv4 header with 2nd byte the ToS field&#34; title=&#34;IPv4 header&#34;&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/ipv6-header.webp&#34; alt=&#34;IPv6 header with 2nd and 3rd nibble the Traffic Classfield&#34; title=&#34;IPv6 header&#34;&gt;&lt;/p&gt;&lt;h4 id=&#34;differentiated-services-code-points&#34;&gt;Differentiated Services Code Points&lt;/h4&gt;&lt;p&gt;Before DSCP was around, the first 3 bits of the ToS field were interpreted to bethe IP Precedence (IPP). These looked a lot like layer-2 CoS. Here, too, 3 bitsmeans 2&lt;sup&gt;3&lt;/sup&gt; = 8 different priority values and, again, Cisco would notallow us to use 6 and 7, because they are reserved for network use). 6 valuesmight not be enough. IPP is now long deprecated and replaced by DifferentiatedServices (DiffServ) RFCs.&lt;/p&gt;&lt;p&gt;DSCP claims the first 6 bits of the ToS field. 6 bits gives us 2&lt;sup&gt;6&lt;/sup&gt; =64 possible code points.&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt; But what does a DSCP value mean? Luckily, the IETFgave DSCPs a unique name, a relative priority value and a drop profile to beused by Weighted Random Early Detection (WRED) so that equipment from differentvendors can interoperate and execute appropriate so-called Per-Hop Behaviors(PHBs). PHBs are actions other than storing and forwarding a message, such asdelaying, discarding packets or changing header fields.&lt;/p&gt;&lt;p&gt;There are  21 DSCPs, their name, their value in decimal and in binary:&lt;/p&gt;&lt;table&gt;  &lt;thead&gt;      &lt;tr&gt;          &lt;th&gt;PHB Name&lt;/th&gt;          &lt;th style=&#34;text-align: center&#34;&gt;Decimal&lt;/th&gt;          &lt;th style=&#34;text-align: center&#34;&gt;Binary&lt;/th&gt;      &lt;/tr&gt;  &lt;/thead&gt;  &lt;tbody&gt;      &lt;tr&gt;          &lt;td&gt;CS0 (default)&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;code&gt;000 000&lt;/code&gt;&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td&gt;CS1&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;code&gt;8&lt;/code&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;code&gt;001 000&lt;/code&gt;&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td&gt;CS2&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;code&gt;16&lt;/code&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;code&gt;010 000&lt;/code&gt;&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td&gt;&amp;hellip;&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;&amp;hellip;&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;&amp;hellip;&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td&gt;CS7&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;code&gt;56&lt;/code&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;code&gt;111 000&lt;/code&gt;&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td&gt;Expedited Forwarding (EF)&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;code&gt;46&lt;/code&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;code&gt;101 110&lt;/code&gt;&lt;/td&gt;      &lt;/tr&gt;  &lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;It&amp;rsquo;s important to note that the DSCP values from the IETF always have the lastbit set to &lt;code&gt;0&lt;/code&gt;. Looking at the table above, we see that DSCP is backwardscompatible with IPP. For example, the IPP value of decimal 5 (&lt;code&gt;101&lt;/code&gt; in binary)was the highest priority under IPP. Under DiffServ it is called ExpeditedForwarding (EF) and it&amp;rsquo;s got the same meaning. &amp;ldquo;CSx&amp;rdquo; stands for &amp;ldquo;ClassSelector&amp;rdquo;.&lt;/p&gt;&lt;p&gt;The table above lists only 9 of the 21 promised DSCPs. The other ones are calledAssured Forwarding (AF) and can take any of these 12 values:&lt;/p&gt;&lt;table&gt;  &lt;thead&gt;      &lt;tr&gt;          &lt;th&gt;Priority&lt;/th&gt;          &lt;th style=&#34;text-align: right&#34;&gt;Low Drop Probability&lt;/th&gt;          &lt;th style=&#34;text-align: right&#34;&gt;Medium Drop Probability&lt;/th&gt;          &lt;th style=&#34;text-align: right&#34;&gt;High Drop Probability&lt;/th&gt;      &lt;/tr&gt;  &lt;/thead&gt;  &lt;tbody&gt;      &lt;tr&gt;          &lt;td&gt;&lt;strong&gt;Class 1&lt;/strong&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;AF11&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;AF12&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;AF13&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td&gt; &lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;code&gt;001 010&lt;/code&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;code&gt;001 100&lt;/code&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;code&gt;001 110&lt;/code&gt;&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td&gt;&lt;strong&gt;Class 2&lt;/strong&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;AF21&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;AF22&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;AF23&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td&gt; &lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;code&gt;010 010&lt;/code&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;code&gt;010 100&lt;/code&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;code&gt;010 110&lt;/code&gt;&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td&gt;&lt;strong&gt;Class 3&lt;/strong&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;AF31&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;AF32&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;AF33&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td&gt; &lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;code&gt;011 010&lt;/code&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;code&gt;011 100&lt;/code&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;code&gt;011 110&lt;/code&gt;&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td&gt;&lt;strong&gt;Class 4&lt;/strong&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;AF41&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;AF42&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;AF43&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td&gt; &lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;code&gt;100 010&lt;/code&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;code&gt;100 100&lt;/code&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;code&gt;100 110&lt;/code&gt;&lt;/td&gt;      &lt;/tr&gt;  &lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;The priority on the left and the first digit in the &lt;code&gt;AF__&lt;/code&gt; scheme is compatiblewith IPP. The drop probability across the top and the second digit in the &lt;code&gt;AF__&lt;/code&gt;scheme indicates how soon traffic labeled with these AF classes is eligible fordropping by the queueing mechanism.&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/random-early-detection.webp&#34; alt=&#34;The RED algorithm using AF markings to decide what packets can get randomlydropped&#34;&gt;&lt;/p&gt;&lt;p&gt;So the second digit gives a queueing algorithm such as Random Early Detection(RED), used for congestion avoidance, the marking it needs. Whenever there aremore than 25 packets queued, for example, and a 26th comes in, any of thepackets marked with &lt;code&gt;AFx3&lt;/code&gt; (second digit &lt;code&gt;3&lt;/code&gt;) have a certain chance of beingdropped.&lt;/p&gt;&lt;p&gt;On IOS, we can classify (match) VoIP and interactive video traffic in class mapswith &lt;code&gt;match&lt;/code&gt; statements, for example &lt;code&gt;match protocol rtp audio&lt;/code&gt;, &lt;code&gt;match cos &amp;lt;0-7&amp;gt;&lt;/code&gt;, &lt;code&gt;match dscp ef&lt;/code&gt; and others. For example:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;! Create class map for voice traffic (RTP audio)class-map match-any VOICE-TRAFFIC match protocol rtp audio match dscp ef match ip precedence 5 match cos 5! Create class map for video conferencing trafficclass-map match-any VIDEO-CONF-TRAFFIC match protocol rtp video match dscp af41 af42 af43 match ip precedence 4&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We mark packets in policy maps:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1(config)#policy-map DEMOR1(config-pmap)#class VOICE-TRAFFICR1(config-pmap-c)#set ?  atm-clp        Set ATM CLP bit to 1  cos            Set IEEE 802.1Q/ISL class of service/user priority  cos-inner      Set Inner CoS  discard-class  Discard behavior identifier  dscp           Set DSCP in IP(v4) and IPv6 packets  fr-de          Set FR DE bit to 1  fr-fecn-becn   SET FR FECN-BECN  ip             Set IP specific values  mpls           Set MPLS specific values  precedence     Set precedence in IP(v4) and IPv6 packets  qos-group      Set QoS Group  vlan-inner     Set Inner Vlan&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here, we can &lt;code&gt;set cos &amp;lt;0-7&amp;gt;&lt;/code&gt;, &lt;code&gt;set dscp &amp;lt;0-63&amp;gt;&lt;/code&gt; or with an IETF mnemonic, or&lt;code&gt;set precedence &amp;lt;0-7&amp;gt;&lt;/code&gt; or with a mnemonic.&lt;/p&gt;&lt;h4 id=&#34;congestion-avoidance-with-explicit-congestion-notification-ecn&#34;&gt;Congestion Avoidance with Explicit Congestion Notification (ECN)&lt;/h4&gt;&lt;p&gt;Now, instead of forcing a TCP Slow Start by dropping packets, it might be betterto just ask a sender to slow down. That&amp;rsquo;s what ECN is all about.&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/ecn-ip.webp&#34; alt=&#34;The IPv4 ToS byte and IPv6 Traffic Class byte showing the last 2 bits beingused for ECT and CE.&#34;&gt;&lt;/p&gt;&lt;p&gt;After enabling ECN on Linux (see below), notice that in the initial TCP SYNsegment, the sender will not set the ECN-Capable Transport (ECT) bit of &lt;code&gt;10&lt;/code&gt;&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt;in the IP header. Instead, it will set the Congestion Window Reduced (CWR) andExplicit Congestion Echo (ECE) flags in the TCP header. If the server supportsECN, it will send back a TCP SYN+ACK segment with the ECE flag set. Finally,when the client sends its next segment, completing the TCP 3-way handshake, nowit will set the ECT bit in the IP header (&lt;code&gt;10&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;Then, when a router is congested or about to be congested, it can set theCongestion Experienced (CE) bit to &lt;code&gt;1&lt;/code&gt; and send the package onwards to itsdestination. The recipient will then set the ECE TCP flag in its ACK back to thesender. The sender notices the ECE bit, reduces its congestion window by halfand sets the CWR flag in its next segment, notifying the receiver that thetraffic flow will slow down a bit.&lt;/p&gt;&lt;p&gt;On Linux, temporarily enable&lt;sup id=&#34;fnref:5&#34;&gt;&lt;a href=&#34;#fn:5&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;5&lt;/a&gt;&lt;/sup&gt; with:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# sysctl net.ipv4.tcp_ecn=1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To see it in action, for IPv4, filter with:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# tcpdump &amp;#39;ip[1] &amp;amp; 0x3 != 0&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For IPv6, filter with:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# tcpdump &amp;#39;ip6[1] &amp;amp; 0x30 != 0&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Filtering the TCP flags under IPv4 in &lt;code&gt;tcpdump&lt;/code&gt; is straightforward:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# tcpdump &amp;#39;tcp[13] &amp;amp; 0xc0 != 0&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, &lt;a href=&#34;https://serverfault.com/questions/1143954/filtering-for-http-methods-in-ipv6-packets-using-tcpdump&#34;&gt;this does not show anything when the network protocol is IPv6&lt;/a&gt;,because IPv6 can use extension headers. Those extension headers are indicated inthe IPv6 Next Header field, creating a chain of headers that &lt;code&gt;tcpdump&lt;/code&gt; iscurrently (version 4.99.5 from April 8 2025) unable to follow. If you know thereare no extension headers involved, &lt;code&gt;ip6[53] &amp;amp; 0xc0 != 0&lt;/code&gt; will address the TCPflags byte. (40 bytes for the IPv6 header. Unreserved flags are the 14th byte inthe TCP header. And since we are counting from 0: &lt;code&gt;40 + 14 - 1 = 53&lt;/code&gt;.)&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/tcp-header.webp&#34; alt=&#34;TCP header&#34; title=&#34;TCP header&#34;&gt;&lt;/p&gt;&lt;p&gt;We can configure a Cisco router to set the CE bit by configuring WRED with&lt;code&gt;random-detect ecn&lt;/code&gt; from policy-map class config mode (see config examplelater). I&amp;rsquo;m not sure, but it may very well be the case that traffic marked withECN, instead of being dropped, always gets the CE bit set when the minimum queuedepth threshold is exceeded. Other, non-ECN traffic is still subject to randomdrops.&lt;/p&gt;&lt;h2 id=&#34;prioritizing-traffic-using-queuing&#34;&gt;Prioritizing Traffic using Queuing&lt;/h2&gt;&lt;p&gt;Whenever a router receives traffic faster than it can send it out of a egressqueue, the packet gets stored in this queue and has to wait for bandwidth tobecome available. A queue is a buffer and has finite capacity. We can havemultiple queues based on our traffic classes. A queuing algorithm determineswhat packet to send next from one of the queues.&lt;/p&gt;&lt;p&gt;An old queuing algorithm supported by Cisco is &lt;strong&gt;Weighted Fair Queuing&lt;/strong&gt; (WFQ)where no single flow of traffic can dominate the bandwidth. Notice that thisworks per-flow, not per-host. A single host with lots of connections can stilldominate the bandwidth and starve other hosts that have less flows. WFQ is stillrecommended for the class-default&lt;sup id=&#34;fnref:6&#34;&gt;&lt;a href=&#34;#fn:6&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;6&lt;/a&gt;&lt;/sup&gt;: all traffic that is not otherwiseclassified.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Class-Based Weighted Fair Queuing&lt;/strong&gt; (CBWFQ) guarantees minimum bandwidth forup to 11 (maximum recommended) classes of traffic. During times of congestion,that minimum bandwidth is guaranteed. If there is no congestion, a class oftraffic could use more bandwidth if it needs it. So we could have a queuecontaining bulk data and guarantee that class 10 &lt;em&gt;or more&lt;/em&gt; Mbps.&lt;/p&gt;&lt;p&gt;I&amp;rsquo;m not completely sure, but according to &lt;a href=&#34;https://www.cisco.com/en/US/docs/ios/12_0t/12_0t5/feature/guide/cbwfq.html&#34;&gt;Cisco documentation&lt;/a&gt;,CBWFQ is configured on an interface automatically when a service policy isapplied to the interface. See configuration example later on.&lt;/p&gt;&lt;p&gt;A normal queue is created by the &lt;code&gt;bandwidth &amp;lt;kbps&amp;gt;&lt;/code&gt; keyword or &lt;code&gt;bandwidth percent &amp;lt;n&amp;gt;&lt;/code&gt;or &lt;code&gt;bandwidth remaining percent &amp;lt;n&amp;gt;&lt;/code&gt; in policy-map class config mode.&lt;/p&gt;&lt;p&gt;The queuing algorithm can also handle a single priority queue. This is usefulfor latency-sensitive applications such as VoIP or video conferencing. Such a&lt;strong&gt;Low-Latency Queue&lt;/strong&gt; (LLQ) gets priority treatment up to a &lt;em&gt;maximum&lt;/em&gt; bandwidthduring times of congestion. The maximum prevents the LLQ from starving all otherqueues when the egress is congested. Excess traffic is dropped by a policer.&lt;/p&gt;&lt;p&gt;A priority queue is created by using the &lt;code&gt;priority &amp;lt;kbps&amp;gt;&lt;/code&gt; keyword (or the&lt;code&gt;[remaining] percent &amp;lt;n&amp;gt;&lt;/code&gt; alternatives) in policy-map class config mode.&lt;/p&gt;&lt;h2 id=&#34;congestion-avoidance--drop-management&#34;&gt;Congestion Avoidance / Drop Management&lt;/h2&gt;&lt;p&gt;Queuing is congestion management (what to do in the face of congestion?).Congestion avoidance are strategies to avoid filling queues to capacity to beginwith. When a queue is full, it start tail-dropping packets: every new packetthat comes in will be dropped. For a protocol like TCP, this will decrease thecongestion window: the sender will reduce the amount of traffic it sends beforewaiting for an acknowledgement (AKA TCP Slow Start). If all TCP flows experienceTCP Slow Start at the same time and trigger their congestion control strategybecause of tail drop, it is called Global Synchronization. This is aninefficient use of bandwidth.&lt;/p&gt;&lt;p&gt;This is where a congestion-avoidance mechanism such as Weighted Random EarlyDetection (WRED) comes into play. It starts randomly dropping some packets whenthe queue depth (amount of packets in a queue) exceeds some defined threshold.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;0                 25                 50                 75|---transmitted---|---some dropped---|xxx all dropped xxx&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s say the minimum threshold is 25 packets. Any packets exceeding that number(26th and on), will be subject to random drops. The drops become increasinglylikely the deeper the queue becomes. The maximum threshold is 50. Any morepackets are not queued, there are unceremoniously dropped.&lt;/p&gt;&lt;p&gt;Randomly dropping some packets triggers the congestion response from some TCPflows (and presumably QUIC flows), so that those flows slow down, whilepreventing Global Synchronization.&lt;/p&gt;&lt;p&gt;Enable WRED with &lt;code&gt;random-detect&lt;/code&gt; in policy-map class configuration mode (seeconfig example below).&lt;/p&gt;&lt;h2 id=&#34;policing-and-shaping&#34;&gt;Policing and Shaping&lt;/h2&gt;&lt;p&gt;Both policing and shaping have the same goal: to prevent traffic from being senttoo rapidly.&lt;/p&gt;&lt;p&gt;Queuing sets a minimum amount of bandwidth we can use; policing and shaping setmaximums. Policing drops traffic that exceeds the speed limit we set&lt;sup id=&#34;fnref1:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;; shapingdelays traffic by temporarily putting it in a queue.&lt;/p&gt;&lt;p&gt;Both policing and shaping use a &amp;ldquo;token bucket&amp;rdquo;. The following formula governsthe token bucket:&lt;/p&gt;&lt;p&gt;CIR = B&lt;sub&gt;c&lt;/sub&gt; / T&lt;sub&gt;c&lt;/sub&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;CIR&lt;/strong&gt; stands for &lt;strong&gt;Committed Information Rate&lt;/strong&gt; and is the average speed overthe interval of a second (the total amount of traffic sent per second).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;B&lt;sub&gt;c&lt;/sub&gt;&lt;/strong&gt; (Committed Burst) is the number of bits deposited in the tokenbucket per second divided by the &lt;strong&gt;T&lt;sub&gt;c&lt;/sub&gt;&lt;/strong&gt; timing interval. If we wantto send 64 kbps and the timing interval is 8, the bucket replenishes with 64,000/ 8 = 8,000 bits per timing interval (or 1/8 of a second). The egress stillsends at line speed (it has no choice), so it will send those bits out as fastas it can and just be silent the rest of the time.&lt;/p&gt;&lt;h2 id=&#34;configuring-qos-on-a-cisco-router&#34;&gt;Configuring QoS on a Cisco Router&lt;/h2&gt;&lt;p&gt;Cisco uses something called a Modular QoS CLI (MQC). It consists of 3 steps:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create class maps that are used to match the traffic we want to apply QoS to.&lt;/li&gt;&lt;li&gt;Create one or more policy maps that take the previously defined class maps toapply a mechanism to such as shaping or policing.&lt;/li&gt;&lt;li&gt;Finally we apply the policy map, usually to a physical interface (but couldbe an SVI, for example).&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&#34;create-class-maps-that-match-traffic&#34;&gt;Create Class Maps that Match Traffic&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1(config)#class-map match-any EMAILR1(config-cmap)#match protocol smtpR1(config-cmap)#match protocol pop3R1(config-cmap)#match protocol imap&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here, we create a class map with a made-up name &lt;code&gt;EMAIL&lt;/code&gt; (this can be anystring; I make it uppercase so that it stands out from any Cisco keywords). Bydefault, all the selectors (&lt;code&gt;match&lt;/code&gt; statements) must match (logical AND):change it to logical OR by setting &lt;code&gt;match-any&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Then, in class map config mode, we can use something called Network-BasedApplication Recognition (NBAR) to match the traffic we want grouped in thismap. There are a lot of matching options:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1(config-cmap)#match ?  access-group         Access group  any                  Any packets  application          Application to match  class-map            Class map  cos                  IEEE 802.1Q/ISL class of service/user priority values  destination-address  Destination address  discard-class        Discard behavior identifier  dscp                 Match DSCP in IPv4 and IPv6 packets  fr-de                Match on Frame-relay DE bit  fr-dlci              Match on fr-dlci  input-interface      Select an input interface to match  ip                   IP specific values  metadata             Metadata to match  mpls                 Multi Protocol Label Switching specific values  not                  Negate this match result  packet               Layer 3 Packet length  precedence           Match Precedence in IPv4 and IPv6 packets  protocol             Protocol  qos-group            Qos-group  service              Service Instance to match  source-address       Source address  vlan                 VLANs to match&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;protocol&lt;/code&gt; matching might simply look at the destination port of the traffic todecide whether or not it should be in this class map. Ubiquitous TLS has madeDPI pretty much a no-go these days, I would imagine. Although, &lt;code&gt;match protocol netflix&lt;/code&gt; is available, so who knows?&lt;/p&gt;&lt;h3 id=&#34;create-policy-maps-that-use-class-maps-to-police-or-shape-traffic&#34;&gt;Create Policy Maps that Use Class Maps to Police or Shape Traffic&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1(config)#policy-map DEMOR1(config-pmap)#class EMAILR1(config-pmap-c)#police 256000&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here, we create a policy map with the made-up name &lt;code&gt;DEMO&lt;/code&gt;. Again, this can beany string; I simply made up &lt;code&gt;DEMO&lt;/code&gt; and uppercased it to avoid confusion withCisco keywords.&lt;/p&gt;&lt;p&gt;Then, we select the class map to apply the policy function to, in our case&lt;code&gt;EMAIL&lt;/code&gt;. We police SMTP, POP3 and IMAP traffic to 256 kbps in times ofcongestion. It&amp;rsquo;s probably a better idea to apply shaping (delaying) and notpolicing (dropping) to email traffic, but this is just for the sake of a simpleexample.&lt;/p&gt;&lt;p&gt;Here, we could also set WRED with the &lt;code&gt;random-detect&lt;/code&gt; keyword and ECN with&lt;code&gt;random-detect ecn&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&#34;apply-policy-maps-to-interfaces&#34;&gt;Apply Policy Maps to Interfaces&lt;/h3&gt;&lt;p&gt;Finally, we apply the policy map to the outgoing side of an interface:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1(config)#int gig 0/2R1(config-int)#service-policy output DEMO&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All email traffic that leaves interface GigabitEthernet0/2 is now policed to amaximum of 256 kbps in time of congestion.&lt;/p&gt;&lt;p&gt;Note that the CBWFQ queuing algorithm is automatically applied.&lt;/p&gt;&lt;h3 id=&#34;verify-our-work&#34;&gt;Verify Our Work&lt;/h3&gt;&lt;p&gt;Show all defined class maps:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#show class-map Class Map match-any class-default (id 0)   Match any  Class Map match-any EMAIL (id 1)   Match protocol smtp   Match protocol pop3   Match protocol imap&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Show all defined policy maps:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#show policy-map  Policy Map DEMO    Class EMAIL     police cir 256000 bc 8000       conform-action transmit        exceed-action drop&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Show statistics about how are policy map is applied on an interface:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#show policy-map interface gig 0/2 GigabitEthernet0/2  Service-policy output: DEMO    Class-map: EMAIL (match-any)        0 packets, 0 bytes      5 minute offered rate 0000 bps, drop rate 0000 bps      Match: protocol smtp        0 packets, 0 bytes        5 minute rate 0 bps      Match: protocol pop3        0 packets, 0 bytes        5 minute rate 0 bps      Match: protocol imap        0 packets, 0 bytes        5 minute rate 0 bps      police:          cir 256000 bps, bc 8000 bytes        conformed 0 packets, 0 bytes; actions:          transmit         exceeded 0 packets, 0 bytes; actions:          drop         conformed 0000 bps, exceeded 0000 bps              Class-map: class-default (match-any)        0 packets, 0 bytes      5 minute offered rate 0000 bps, drop rate 0000 bps      Match: any &lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;books-to-read&#34;&gt;Books to Read&lt;/h2&gt;&lt;p&gt;This post just barely touched the surface of configuring QoS on Cisco devices.The following books came recommended if you really want to get started:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://community.cisco.com/kxiwq67737/attachments/kxiwq67737/5991-discussions-wan-routing-switching/68482/1/18318-QOS%20Solution%20Reference%20Network%20Design%20Guide.pdf&#34;&gt;Enterprise QoS Solution Reference Network Design Guide&lt;/a&gt;&lt;/li&gt;&lt;li&gt;End-to-End QoS Network Design, 2nd edition (Cisco, 2013)&lt;/li&gt;&lt;/ul&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai spell: --&gt;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&lt;hr&gt;&lt;ol&gt;&lt;li id=&#34;fn:1&#34;&gt;&lt;p&gt;This post was inspired by &lt;a href=&#34;https://www.kwtrain.com/ccna-video-series&#34;&gt;Kevin Wallace&amp;rsquo;s CCNA videos about QoS&lt;/a&gt; andthe official CCNA 200-301 study guide by Wendell Odom. The pictures areblatantly stolen from Kevin Wallace&amp;rsquo;s video and the official study guide; Ihope not enough people read this blog for that to matter. Otherwise I haveto create my own pictures and I am pressed for time currently because myCCNA exam is soon. You could either buy the videos from Kevin Wallacedirectly or subscribe to O&amp;rsquo;Reilly. Highly recommended.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:2&#34;&gt;&lt;p&gt;Or it may just (re)mark the traffic or do nothing and just measure it forlater reporting.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref1:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:3&#34;&gt;&lt;p&gt;Cisco has a design recommendation to not use over 11 different classes oftraffic.&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:4&#34;&gt;&lt;p&gt;In reality, the ECN bit pattern of &lt;code&gt;10&lt;/code&gt; and &lt;code&gt;01&lt;/code&gt; are treated the same.Both indicate that the sender is capable of doing ECN Transport. To becomplete, &lt;code&gt;00&lt;/code&gt; means that the sender is not capable, and &lt;code&gt;11&lt;/code&gt; means that thesender is capable and a router was congested and set the other bit to &lt;code&gt;1&lt;/code&gt; aswell.&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:5&#34;&gt;&lt;p&gt;On Arch, &lt;a href=&#34;https://wiki.archlinux.org/title/Sysctl&#34;&gt;make permanent&lt;/a&gt; by adding to &lt;code&gt;/etc/sysctl.d/99-sysctl.conf&lt;/code&gt;and load it with &lt;code&gt;sysctl --system&lt;/code&gt;.&amp;#160;&lt;a href=&#34;#fnref:5&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:6&#34;&gt;&lt;p&gt;Create a &lt;code&gt;policy-map &amp;lt;WORD&amp;gt;&lt;/code&gt; and then &lt;code&gt;class class-default&lt;/code&gt; and then&lt;code&gt;fair-queue&lt;/code&gt; to enable WFQ for all unclassified traffic.&amp;#160;&lt;a href=&#34;#fnref:6&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>OSPF</title>
       <link>https://relentlesscoding.com/posts/ospf/</link>
       <pubDate>Fri, 16 May 2025 12:58:33 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/ospf/</guid>
       <description>&lt;p&gt;What follows is basically a summary of what I have learned about OSPF during myCCNA studies.&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;routing-protocols-and-ospf&#34;&gt;Routing Protocols and OSPF&lt;/h2&gt;&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Open_Shortest_Path_First&#34;&gt;OSPF&lt;/a&gt; is a routing protocol. Version 2 is only concerned with IPv4, whereasversion 3 added IPv6 support.&lt;/p&gt;&lt;p&gt;OSPF is an Interior Gateway Protocol (IGP).&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; Its task as a routing protocolis to make sure all routers in an autonomous system (AS; network under singleadministrative control) can route packets to all networks inside that AS andpossibly to the internet. This is contrasted to an Exterior Gateway Protocol(EGP) such as BGP (Border Gateway Protocol) that is concerned with advertisingroutes between ASs.&lt;/p&gt;&lt;p&gt;OSPF is a link-state protocol. It calculates the cost of an outgoing link&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;and uses that information to determine the best route from source todestination. This is opposed a distance-vector protocol such as RIP. (&amp;ldquo;Distancevector&amp;rdquo; is fancy-talk for counting the number of routers [hops] between a sourceand a destination network.) RIP does not take into account the throughput oflinks; if you have a 1-hop connection to the destination subnet over a very slowlink or a 2-hop connection over gigabit links, the RIP process will put the veryslow link in forwarding table (FIB)&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;h2 id=&#34;enabling-ospf&#34;&gt;Enabling OSPF&lt;/h2&gt;&lt;p&gt;When you start an OSPF process on a router with &lt;code&gt;router ospf &amp;lt;pid&amp;gt;&lt;/code&gt; in globalconfiguration mode, it will dynamically discover any OSPF neighbors. It doesthis by sending out OSPF Hello packets. If the settings of the first Hellomatches the OSPF settings in the neighboring router that is also running OSPF,it will send back an OSPF Hello. After this handshake, a 2-way state isreached.&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Each router has a router ID (RID). The RID is either&lt;/p&gt;&lt;ol&gt;&lt;li&gt;explicitly configured in router OSPF submode with &lt;code&gt;router-id &amp;lt;rid&amp;gt;&lt;/code&gt;; or&lt;/li&gt;&lt;li&gt;the router selects the highest/greatest IPv4 address on an up/up loopbackinterface (&lt;code&gt;int loopback &amp;lt;n&amp;gt;&lt;/code&gt; from global configuration mode and then &lt;code&gt;ip address &amp;lt;address&amp;gt; &amp;lt;mask&amp;gt;&lt;/code&gt;); or&lt;/li&gt;&lt;li&gt;the highest/greatest IPv4 address on another up/up non-loopback interface.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;If the RID is not configured explicitly, and there aren&amp;rsquo;t any interfaces, youcannot start an OSPF process.&lt;/p&gt;&lt;h2 id=&#34;exchanging-routing-information-databases&#34;&gt;Exchanging Routing Information Databases&lt;/h2&gt;&lt;p&gt;Once the router has a neighbor relationship with another router, they will startexchanging routing information. Each router contains a link-state database(LSDB) containing link-state advertisements (LSAs) of several types, 3 of whichwe are currently concerned with for the CCNA exam:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Type 1 LSAs contain information about neighboring routers and the cost of theoutgoing interface to reach those neighbors and the type of connection(broadcast, point-to-point).&lt;/li&gt;&lt;li&gt;Type 2 LSAs are only created on broadcast links by the DR and contain theDR&amp;rsquo;s IP address and a list of all the other routers on the segment.&lt;/li&gt;&lt;li&gt;Type 3 LSAs are generated by Area Border Routers (ABRs) that are part of 2 ormore areas and summarize networks in another area. (ABRs are discussed lateron in this post.)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;On a point-to-point link (having only 2 routers ever on the same link), a masterand a slave are chosen. The master initiates the exchange of databaseinformation by sending a database description (DBD) message to the slave. TheDBD contains only header information, not the actual data structures containingall information. The slave looks at the DBD and, if it finds it misses someroute information, sends link-state requests (LSRs) to the master. The masterresponds with link-state update packets containing the actual LSAs. The slavealso sends the headers of its database to the master, and the same processrepeats in the other direction.&lt;/p&gt;&lt;p&gt;During this exchange, the routers will transition from the 2-way state toExStart, Exchange, Loading and finally the Full state.&lt;/p&gt;&lt;h2 id=&#34;network-types&#34;&gt;Network Types&lt;/h2&gt;&lt;p&gt;In the most simple case, on a serial link, there is just a point-to-point linkbetween 2 routers. The routers become neighbors and exchange BDBs.&lt;/p&gt;&lt;p&gt;On Ethernet links, however, by default the broadcast network type is used.&lt;sup id=&#34;fnref:5&#34;&gt;&lt;a href=&#34;#fn:5&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;5&lt;/a&gt;&lt;/sup&gt;Because more than 2 routers can connect to the same subnet, the number ofdatabase exchanges grows exponentially if each router has to exchange itsdatabase information with every other router. This mesh will have &lt;code&gt;n * (n-1) / 2&lt;/code&gt; connections. To combat this exploding overhead, on a broadcast medium such asEthernet, a Designated Router (DR) and a Backup DR (BDR) are elected&lt;sup id=&#34;fnref:6&#34;&gt;&lt;a href=&#34;#fn:6&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;6&lt;/a&gt;&lt;/sup&gt;. Eachrouter still forms neighbor relationships with every other router, but will onlyexchange database information with the DR and BDR. This reduces the number ofexchanges to &lt;code&gt;2n - 3&lt;/code&gt;. If you have 48 routers segment, a full mesh requires&lt;code&gt;48 * (48-1) / 2 = 1128&lt;/code&gt; exchanges, whereas with a DR and BDR you would needonly &lt;code&gt;2 * 48 - 3 = 93&lt;/code&gt; exchanged. Huge savings.&lt;/p&gt;&lt;p&gt;What happens when you set one interface to a point-to-point network and leavethe other to broadcast? Confusingly, they will still form a full adjacency andexchange their LSDB, but the routes will not show up under &lt;code&gt;show ip route ospf&lt;/code&gt;.&lt;sup id=&#34;fnref:7&#34;&gt;&lt;a href=&#34;#fn:7&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Also, if an interface is known to never encounter another OSPF-enabled router onits link, you can set the interface to passive mode with &lt;code&gt;passive-interface &amp;lt;type&amp;gt; &amp;lt;number&amp;gt;&lt;/code&gt; in router OSPF submode. It will still advertise this subnet toother routers over other interfaces, but it will not bother to send Hellos out.&lt;/p&gt;&lt;h2 id=&#34;configure-ospf&#34;&gt;Configure OSPF&lt;/h2&gt;&lt;p&gt;Enable OSPF on the router:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#router ospf 1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The PID is locally significant: it does not need to be the same on anotherrouter to form a neighbor relationship.&lt;/p&gt;&lt;p&gt;There are 2 ways to configure OSPF on an interface: an interface subcommand anda router OSPF subcommand. The first method more intuitive:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1(config)#interface gig 0/0/1R1(config-if)#ip ospf 1 area 0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;1&lt;/code&gt; refers to the locally-significant process ID of OSPF. It could beanything between 1-65535 as long as it matches the &lt;code&gt;router ospf &amp;lt;pid&amp;gt;&lt;/code&gt;. I haveyet to understand why you would want to have multiple OSPF processes running,but know that you can.&lt;/p&gt;&lt;p&gt;&lt;code&gt;area 0&lt;/code&gt; indicates which area this interface belongs to. Multi-area OSPF becomesinteresting as a way to reduce the time needed to run &lt;a href=&#34;https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm&#34;&gt;Dijkstra&amp;rsquo;sAlgorithm&lt;/a&gt;. An (old) recommendation is to start using multiple areasonce you have more than 50 routers in a single area. If you have multiple areas,then one area needs to be area 0 and all other areas need to connect to it.Routers on the border of areas are called Area Border Routers (ABRs). They sendroute aggregation (Type 3) messages advertising that all traffic to specificsubnets need to go through them. Instead of learning the topology of those otherareas, routers just need to learn those aggregated routes.&lt;/p&gt;&lt;p&gt;The old way to specify which interfaces should be included in OSPF is the&lt;code&gt;network &amp;lt;network&amp;gt; &amp;lt;wildcard-mask&amp;gt; area &amp;lt;area-number&amp;gt;&lt;/code&gt; router OSPF subcommand.For example, if we have interface g0/1 and g0/2 with IPv4 addresses 192.1.0.1/24and 192.2.0.1/24, respectively, we would include these interfaces with thefollowing &lt;code&gt;network&lt;/code&gt; commands:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1(config)#router ospf 1R1(config-router)#network 192.1.0.0 0.0.0.255 area 0R1(config-router)#network 192.2.0.0 0.0.0.255 area 0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Crucially, the &lt;code&gt;network&lt;/code&gt; command does not advertise these routes. It just says:&amp;ldquo;if there are any interfaces that match this network and wildcard mask,advertise their networks&amp;rdquo;. This becomes more clear if we use a wildcard mask ofall zeroes:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1(config-router)#network 192.2.0.1 0.0.0.0 area 0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This would only enable OSPF on the interface that matches the given IPv4 addressexactly. (Conversely, to include all interfaces, use &lt;code&gt;network 0.0.0.0 255.255.255.255 area &amp;lt;n&amp;gt;&lt;/code&gt;.) Again, the router will not just advertise192.2.0.1/32 in this case, but whatever the subnet happens to be configured onthe matching interface (for example 192.2/16).&lt;/p&gt;&lt;p&gt;Related this this, an interface needs to have OSPF enabled before its subnet isadvertised over other links. If we forget to enable OSPF on a link, the subnetis not included. Other routers in the area will not know how to send trafficthat way. So, if we have a subnet with no other routers on it and thereforetrying to form neighbor relationships does not make sense, we still need toenable OSPF on the interface. To prevent the overhead of OSPF Hellos, we canconfigure the interface to be &amp;ldquo;passive&amp;rdquo; with &lt;code&gt;passive-interface &amp;lt;type&amp;gt; &amp;lt;number&amp;gt;&lt;/code&gt;in router OSPF mode.&lt;/p&gt;&lt;p&gt;By the way, a wildcard mask (also used with ACLs), is the inverse of a subnetmask. To quickly calculate a wildcard mask, take the dotted-quad broadcastaddress of &lt;code&gt;255.255.255.255&lt;/code&gt; and subtract the subnet mask. In our case, thesubnet mask is &lt;code&gt;255.255.255.0&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;  255.255.255.255- 255.255.255.0-----------------    0.  0.  0.255&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://relentlesscoding.com/posts/cisco-ios-authentication/&#34;&gt;A previous post has more details on how to interpret wildcardmasks&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&#34;verify&#34;&gt;Verify&lt;/h2&gt;&lt;p&gt;Show the OSPF neighbors:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#show ip ospf neighborNeighbor ID     Pri   State           Dead Time   Address         Interface172.16.12.2       1   FULL/BDR        00:00:37    172.16.12.2     GigabitEthernet0/0/0172.16.13.3       1   FULL/BDR        00:00:37    172.16.13.3     GigabitEthernet0/1/0172.16.14.4       1   FULL/DR         00:00:37    172.16.14.4     GigabitEthernet0/2/0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This shows the RID, the priority, the state and function of the OSPF neighbors,respectively, as well as the interface off of which the neighbor lives. Here,the neighbors use the numerically highest IPv4 address as their RID. A state ofDR or BDR means &lt;em&gt;the neighbor&lt;/em&gt; has that role, &lt;em&gt;not the current router&lt;/em&gt;. Forexample, the neighbor with RID 172.16.12.2 is a BDR.&lt;/p&gt;&lt;p&gt;To see which interfaces on the router have OSPF enable and what role thoseinterfaces have:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#show ip ospf interface briefInterface     PID   Area      IP Address/Mask          Cost  State  Nbrs F/CGig0/2/0        1   0       172.16.14.1/255.255.255.0   1      BDR  1/1Gig0/1/0        1   0       172.16.13.1/255.255.255.0   1       DR  1/1Gig0/0/0        1   0       172.16.12.1/255.255.255.0   1       DR  1/1Gig0/0          1   0        172.16.1.1/255.255.255.0   1       DR  0/0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;R1 is the DR on Gig0/1/0, for example.&lt;/p&gt;&lt;p&gt;Show router learned through OSPF:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#show ip route ospf     172.16.0.0/16 is variably subnetted, 10 subnets, 2 masksO       172.16.2.0 [110/2] via 172.16.12.2, 00:06:57, GigabitEthernet0/0/0O       172.16.4.0 [110/5] via 172.16.12.2, 00:06:57, GigabitEthernet0/0/0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;An administrative distance (AD) of 110 is the default believability used byCisco gear for routes learned through OSPF (some other ADs are 0 for directlyconnected routes, 1 for statically configured routes, 90 for EIGRP). The valueafter the forward slash &lt;code&gt;/&lt;/code&gt; indicates the routing-protocol specific metric. Thisis the cost OSPF calculated to take us to this subnet.&lt;/p&gt;&lt;h2 id=&#34;troubleshooting&#34;&gt;Troubleshooting&lt;/h2&gt;&lt;h3 id=&#34;routers-do-not-become-neighbors&#34;&gt;Routers do not become neighbors&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;The routers are in different areas. An Area Border Router &lt;em&gt;is the border&lt;/em&gt;,meaning it is in 2 or more areas, but still needs to be in the same area asanother router to form neighbor relationships. See area settings with &lt;code&gt;show running-config | section ospf&lt;/code&gt;, &lt;code&gt;show ip ospf interface [brief]&lt;/code&gt; or &lt;code&gt;show ip protocols&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;The RIDs are not unique; 2 or more routers have the same RID.&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&#34;no-ospf-routes-show-up-in-the-fib&#34;&gt;No OSPF Routes Show Up in the FIB&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;The MTU might be configured differently. List the MTU with &lt;code&gt;show interfaces &amp;lt;type&amp;gt; &amp;lt;number&amp;gt;&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;One router has network type broadcast, whereas the other has point-to-pointconfigured. &lt;code&gt;show ip ospf neighbor&lt;/code&gt; lists the state of the neighbor.&lt;/li&gt;&lt;/ul&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai spell: --&gt;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&lt;hr&gt;&lt;ol&gt;&lt;li id=&#34;fn:1&#34;&gt;&lt;p&gt;&amp;ldquo;Gateway&amp;rdquo; is another word for &amp;ldquo;router&amp;rdquo;.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:2&#34;&gt;&lt;p&gt;Influence that cost with the &lt;code&gt;ip ospf cost &amp;lt;1-65535&amp;gt;&lt;/code&gt; or the &lt;code&gt;bandwidth &amp;lt;1-10000000&amp;gt;&lt;/code&gt; interface subcommand, or the &lt;code&gt;auto-cost reference-bandwidth &amp;lt;bandwidth-in-mbps&amp;gt;&lt;/code&gt; command in router OSPF mode. The last method ispreferred over setting the &lt;code&gt;bandwidth&lt;/code&gt; on individual interfaces. Thereference bandwidth is used in the formula &lt;code&gt;ref_bandwidth / int_bandwidth&lt;/code&gt;.Setting it to 100,000 Mbps will result in a cost of 100 for Gigabit Ethernetinterfaces. On modern hardware, this makes sense. It is not recommended tochange the &lt;code&gt;bandwidth&lt;/code&gt; setting on individual interfaces. Although this hasno effect on the actual speed of the interface, it might be used by morethan just OSPF.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:3&#34;&gt;&lt;p&gt;A router contains both a routing information base (RIB) and a forwardinginformation base (FIB). The RIB contains all the routes learned by therouter. These could be directly connected routes, statically configuredroutes and dynamically learned routes. The router then selects the bestroutes to a destination, ignoring worse, duplicate routes.&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:4&#34;&gt;&lt;p&gt;2 routers become neighbors if:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Their interfaces are up/up&lt;/li&gt;&lt;li&gt;ACLs do not filter routing protocol messages&lt;/li&gt;&lt;li&gt;Interfaces are in the same subnet&lt;/li&gt;&lt;li&gt;They can authenticate to each other&lt;/li&gt;&lt;li&gt;Hello and hold/dead timers match&lt;/li&gt;&lt;li&gt;RIDs are unique&lt;/li&gt;&lt;li&gt;Interfaces are in the same area&lt;/li&gt;&lt;li&gt;OSPF process is running (not shutdown)&lt;/li&gt;&lt;li&gt;Interfaces have the same MTU&lt;/li&gt;&lt;li&gt;Interfaces have same OSPF network type (broadcast, point-to-point)&lt;/li&gt;&lt;/ol&gt;&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/li&gt;&lt;li id=&#34;fn:5&#34;&gt;&lt;p&gt;Change with &lt;code&gt;ip ospf network {broadcast | point-to-point}&lt;/code&gt; in interfacemode.&amp;#160;&lt;a href=&#34;#fnref:5&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:6&#34;&gt;&lt;p&gt;Influence the election by changing the RID to a numerically higher numberor setting the priority of the router with &lt;code&gt;ip ospf priority &amp;lt;0-255&amp;gt;&lt;/code&gt; ininterface mode. That number defaults to &lt;code&gt;1&lt;/code&gt;. A higher number means higherpriority (contrast that with STP where a lower number means higherpriority). Setting the value to &lt;code&gt;0&lt;/code&gt; means &amp;ldquo;do not participate in election&amp;rdquo;.&amp;#160;&lt;a href=&#34;#fnref:6&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:7&#34;&gt;&lt;p&gt;There are more network types (non-broadcast multi-access [NBMA],point-to-multipoint, point-to-multipoint non-broadcast), but that is outsidethe scope of the CCNA exam, so we are not interested at this point.&amp;#160;&lt;a href=&#34;#fnref:7&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Convert Cisco Lightweight AP to Autonomous</title>
       <link>https://relentlesscoding.com/posts/cisco-convert-lightweight-ap-to-autonomous/</link>
       <pubDate>Sun, 04 May 2025 07:09:27 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/cisco-convert-lightweight-ap-to-autonomous/</guid>
       <description>&lt;p&gt;If you have a Cisco AP that has CAP in the model, you have a so-calledlightweight AP that is supposed to be controlled by a wireless controller (WLC).I was able to convert my AIR-CAP2602E-E-K9 to autonomous mode by loadingdifferent firmware. This post discusses how to do that.&lt;/p&gt;&lt;!-- more --&gt;&lt;p&gt;A &lt;code&gt;show version&lt;/code&gt; indicated that I had &lt;code&gt;ap3g2-k9w8-*&lt;/code&gt; running &amp;ndash; a CAP image. Ineeded to download a &lt;code&gt;ap3g-k9w7-*&lt;/code&gt; (notice the &lt;code&gt;7&lt;/code&gt; instead of &lt;code&gt;8&lt;/code&gt;) image and getit on the AP.&lt;/p&gt;&lt;p&gt;Instructions:&lt;/p&gt;&lt;p&gt;First, start a TFTP server on the same LAN the AP lives on and serve the desiredfirmware image. Then, remove power from lightweight AP. &lt;a href=&#34;https://web.archive.org/web/20250430201542/https://www.cisco.com/c/en/us/td/docs/wireless/access_point/2800/quick/guide/ap2800iegetstart.html&#34;&gt;Now, press and hold themode button. Finally, apply power and wait 25 seconds until the status light onthe front turns to solid red.&lt;/a&gt; If you only see green, you did not push themode button correctly. (If you just want to reset the configuration to factorydefaults, wait until you are in recovery mode and type, &lt;code&gt;delete flash:config.txt.bak&lt;/code&gt; and then &lt;code&gt;reset&lt;/code&gt;.)&lt;/p&gt;&lt;p&gt;The console confirms how long you pressed the button:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;button is pressed, wait for button to be released...button pressed for 24 seconds&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The AP will now try to download &lt;a href=&#34;tftp://255.255.255.255/ap3g2-k9w7-tar.default&#34;&gt;tftp://255.255.255.255/ap3g2-k9w7-tar.default&lt;/a&gt;.I was, however, unable to see that IP broadcast in &lt;code&gt;tcpdump&lt;/code&gt; on my Linuxmachine, and so the connection attempt timed out. I was then shuttled intorecovery mode. Here, I first made sure the things I need were loaded, such as anEthernet connection, the flash file system and TFTP.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ap: ether_initap: flash_initap: tftp_init&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, I set my IP address and subnet mask. If you connect to the AP via aswitch, they need to be on the same VLAN/network as the TFTP server. My TFTPserver answered to 10.0.0.2.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ap: set IP_ADDR 10.0.0.1ap: set SUBMASK 255.255.255.0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, we can download and extract the image in one go:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ap: tar -xtract tftp://10.0.0.2/ap3g2-k9w7-tar.153-3.JF10.tar flash:&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If there is not enough space, &lt;code&gt;format flash:&lt;/code&gt; first, after you have made sureyou have a backup of the original firmware image.&lt;/p&gt;&lt;p&gt;Finally, let&amp;rsquo;s set the boot image and reboot:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ap: set boot flash:ap3g2-k9w7-mx.153-3.JF10/ap3g2-k9w7-mx.153-3.JF10ap: boot&lt;/code&gt;&lt;/pre&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai spell: --&gt;</description>
     </item>
   
     <item>
       <title>Cisco LAN Routing</title>
       <link>https://relentlesscoding.com/posts/lan-routing/</link>
       <pubDate>Thu, 24 Apr 2025 11:23:15 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/lan-routing/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s look at several ways to route between VLANs in the Cisco world.&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;router-on-a-stick-roas&#34;&gt;Router on a Stick (ROAS)&lt;/h2&gt;&lt;p&gt;A router is connected to a switch. If separate VLANs on separate IP subnets wantto talk to each other, they have to go through the router. The router has a VLANtrunk to the switch. The router itself needs an IP address in every VLAN itroutes. These IP addresses are the default routes for the end hosts.&lt;/p&gt;&lt;p&gt;On the router, you create subinterfaces by putting a &lt;code&gt;.&amp;lt;n&amp;gt;&lt;/code&gt; after the interfacetype and number, where &lt;code&gt;n&lt;/code&gt; can be any number. By doing so, you immediatelycreate the interface.&lt;/p&gt;&lt;p&gt;You tell the subinterface which VLAN ID all incoming frames are tagged with andall outgoing frames will be tagged with:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3(config-if)#encapsulation dot1q 20&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You then assign an IP address to that interface:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3(config-if)#ip addr 10.10.20.1 255.255.255.0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, make sure the physical interface is not &lt;code&gt;shutdown&lt;/code&gt;.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3(config)#int g0/0/0L3(config-if)#no shutdown&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Verify:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Router#sh ip int brInterface               IP-Address      OK? Method Status                Protocol GigabitEthernet0/0/0    unassigned      YES unset  up                    up GigabitEthernet0/0/0.10 10.10.10.1      YES manual up                    up GigabitEthernet0/0/0.20 10.10.20.1      YES manual up                    up GigabitEthernet0/0/1    unassigned      YES unset  administratively down down Vlan1                   unassigned      YES unset  administratively down down&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;switched-virtual-interfaces-svis&#34;&gt;Switched Virtual Interfaces (SVIs)&lt;/h2&gt;&lt;p&gt;Layer-3 switches do both layer-2 switching and layer-3 IP routing. When a framecomes in, it will act like a switch, unless the MAC address on the frame isaddressed to the switch itself. In that case, the switch will de-encapsulate theframe and inspect the package. If addressed to the switch itself, it will passthe package to a locally running process, else it will perform its routingfunction and forward the package out one of its interfaces.&lt;/p&gt;&lt;p&gt;The created SVI is not attached to any physical port. Instead, whenever a frametagged with the right VLAN ID comes in on &lt;em&gt;any&lt;/em&gt; of the ports, the switch willtreat it as if it came in from that virtual interface. It will switch the frameto any other interface associated with that VLAN ID. The creation of a virtualinterface will give the layer-3 switch&amp;rsquo;s routing logic an entrypoint into theVLAN.&lt;/p&gt;&lt;p&gt;In order for a layer-3 switch to perform layer-3 routing:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3(config)#ip routing&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Configure the VLAN interface and assign an IP address to it:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3(config)#vlan 10L3(config)#int vlan 10L3(config-if)#ip address 10.10.10.1 255.255.255.0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Verify:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3#sh ip route | i 10.10.10C       10.10.10.0/24 is directly connected, Vlan10&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If the routing table is empty, you might have forgotten to enable &lt;code&gt;ip routing&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Other gotchas:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;VLANs must be defined on the local switch.&lt;/li&gt;&lt;li&gt;The switch must have at least 1 up/up interfaces using the VLAN (up/up accessinterface using that VLAN or a trunk interface for which the VLAN is allowedand is STP forwarding).&lt;/li&gt;&lt;li&gt;&lt;code&gt;vlan &amp;lt;n&amp;gt;&lt;/code&gt; must be &lt;code&gt;no shutdown&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;int vlan &amp;lt;n&amp;gt;&lt;/code&gt; must be &lt;code&gt;no shutdown&lt;/code&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&#34;layer-3-switch-routed-ports&#34;&gt;Layer-3 Switch Routed Ports&lt;/h2&gt;&lt;p&gt;If we want to route traffic between 2 devices that do routing, we might not needVLAN information. In that case, we can use routed interfaces.&lt;/p&gt;&lt;p&gt;A layer-3 switch can disable the switching behavior of a port (&lt;code&gt;no switchport&lt;/code&gt;).This makes it a routed port. It will no longer be forwarding frames based ontheir MAC address. Instead, it will only accept frames addressed to the routedport, de-encapsulate the frame and take it from there.&lt;/p&gt;&lt;p&gt;Configure:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3(config)#int g1/1/1L3(config-if)#no switchportL3(config-if)#ip addr 10.100.100.1 255.255.255.252&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can verify in multiple ways:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3#sh int statusPort      Name  Status       Vlan       Duplex  Speed Type! ... snip ...Gig1/1/1        connected    routed     auto    auto  10/100BaseTX&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;show interfaces&lt;/code&gt; should show an IP address (because switch ports do not show IPaddresses on a physical interface).&lt;/p&gt;&lt;p&gt;&lt;code&gt;show ip route&lt;/code&gt; should show the physical interface as an outgoing interface inroutes (again, switch ports are not listed as outgoing ports).&lt;/p&gt;&lt;p&gt;&lt;code&gt;show interfaces &amp;lt;type&amp;gt; &amp;lt;number&amp;gt; switchport&lt;/code&gt; should show short output confirmingthe port is not a switch port.&lt;/p&gt;&lt;p&gt;Interface &lt;code&gt;Gig1/1/1&lt;/code&gt; has &lt;code&gt;routed&lt;/code&gt; under &lt;code&gt;Vlan&lt;/code&gt;, indicating that it does notparticipate in any VLANs.&lt;/p&gt;&lt;p&gt;(Make a router port a switch port again by issuing the &lt;code&gt;switchport&lt;/code&gt; command ininterface configuration mode.)&lt;/p&gt;&lt;p&gt;Incidentally, you can create a &lt;a href=&#34;https://relentlesscoding.com/posts/etherchannel/&#34;&gt;layer-3 EtherChannel&lt;/a&gt; bycreating a &lt;code&gt;channel-group &amp;lt;n&amp;gt; mode &amp;lt;active|desirable|on&amp;gt;&lt;/code&gt; on 2 or more routedports. Make sure to also issue &lt;code&gt;no switchport&lt;/code&gt; on the &lt;code&gt;int port-channel &amp;lt;n&amp;gt;&lt;/code&gt;itself and double check that both &lt;code&gt;speed&lt;/code&gt; and &lt;code&gt;duplex&lt;/code&gt; are set to &lt;code&gt;auto&lt;/code&gt; on thephysical ports.&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai spell: --&gt;</description>
     </item>
   
     <item>
       <title>EtherChannel</title>
       <link>https://relentlesscoding.com/posts/etherchannel/</link>
       <pubDate>Thu, 24 Apr 2025 08:55:30 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/etherchannel/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s have a look at how we can use redundant layer-2 links between 2 Ciscodevices.&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;benefits-of-using-etherchannel&#34;&gt;Benefits of Using EtherChannel&lt;/h2&gt;&lt;p&gt;A layer-2 EtherChannel aggregates up to 8 links. This provides redundancy,because 1 of the links can fail or go down, and traffic will still flow over theother links. It also provides greater bandwidth, which is especially importantover a trunk link, such as in a Router-on-a-Stick (ROAS) configuration.&lt;/p&gt;&lt;p&gt;Without an EtherChannel, the redundant links would still work and, in case of alink failure, the other link would spring into action. But STP would block allbut 1 link. So, in order to increase bandwidth, we need EtherChannel.&lt;/p&gt;&lt;h2 id=&#34;configure-a-layer-2-etherchannel&#34;&gt;Configure a Layer-2 EtherChannel&lt;/h2&gt;&lt;p&gt;Let&amp;rsquo;s create a layer-2 EtherChannel between a multilayer switch called &lt;code&gt;L3&lt;/code&gt; anda switch. First, a couple of things should match on the physical ports,specifically:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;speed&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;duplex&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;switchport access vlan &amp;lt;n&amp;gt;&lt;/code&gt; and&lt;/li&gt;&lt;li&gt;&lt;code&gt;spanning-tree cost &amp;lt;n&amp;gt;&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3(config)#int range f 0/1-2L3(config)#speed autoL3(config)#duplex autoL3(config)#mdix autoL3(config-if-range)#channel-group 1 mode ?  active     Enable LACP unconditionally  auto       Enable PAgP only if a PAgP device is detected  desirable  Enable PAgP unconditionally  on         Enable Etherchannel only  passive    Enable LACP only if a LACP device is detected&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Confusingly, you configure EtherChannels from global config mode under&lt;code&gt;channel-group&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;A &lt;code&gt;channel-group&lt;/code&gt; takes a number. These numbers do not need to be the same onboth sides of the link.&lt;/p&gt;&lt;p&gt;We can let the devices negotiate the EtherChannel by using Cisco&amp;rsquo;s proprietaryPAgP or the standard LACP. Make sure to use the same protocol on both ends ofthe links. LACP &lt;code&gt;active&lt;/code&gt; and PAgP &lt;code&gt;desirable&lt;/code&gt; initiate link aggregation. LACP&lt;code&gt;passive&lt;/code&gt; and PAgP &lt;code&gt;auto&lt;/code&gt; will create an EtherChannel if the other end initiatesit but will do nothing otherwise. &lt;code&gt;on&lt;/code&gt; always creates the EtherChannelregardless of what the other end does. I do not think this should be used,though, because you might forget to configure the other end. The result will bethat you have a device that load balances over 2 links, while the other endtreats them as separate links. This might result in STP blocking a port,out-of-order packets and layer-2 loops (in short, an unstable network).&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3(config-if-range)#channel-group 1 mode activeCreating a port-channel interface Port-channel 1%LINEPROTO-5-UPDOWN: Line protocol on Interface FastEthernet0/1, changed state to down%LINEPROTO-5-UPDOWN: Line protocol on Interface FastEthernet0/1, changed state to up%LINEPROTO-5-UPDOWN: Line protocol on Interface FastEthernet0/2, changed state to down%LINEPROTO-5-UPDOWN: Line protocol on Interface FastEthernet0/2, changed state to up&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Verify that the &lt;code&gt;port-channel&lt;/code&gt; got created and is working:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3#sh ether summaryFlags:  D - down        P - in port-channel        I - stand-alone s - suspended        H - Hot-standby (LACP only)        R - Layer3      S - Layer2        U - in use      f - failed to allocate aggregator        u - unsuitable for bundling        w - waiting to be aggregated        d - default portNumber of channel-groups in use: 1Number of aggregators:           1Group  Port-channel  Protocol    Ports------+-------------+-----------+----------------------------------------------1      Po1(SD)           LACP   Fa0/1(I) Fa0/2(I)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is not good. The &lt;code&gt;Port-channel&lt;/code&gt; indicates the link is down. That is becausewe have not configured the other side yet. Yet another indication is thespanning-tree status, where our &lt;code&gt;port-channel&lt;/code&gt; does not show up and instead wesee the composing interfaces &lt;code&gt;Fa0/1&lt;/code&gt; and &lt;code&gt;Fa0/2&lt;/code&gt;.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#sh spanning-tree VLAN0001  Spanning tree enabled protocol ieee  Root ID    Priority    32769             Address     0001.640A.DB74             Cost        19             Port        1(FastEthernet0/1)             Hello Time  2 sec  Max Age 20 sec  Forward Delay 15 sec  Bridge ID  Priority    32769  (priority 32768 sys-id-ext 1)             Address     0001.649C.A46E             Hello Time  2 sec  Max Age 20 sec  Forward Delay 15 sec             Aging Time  20Interface        Role Sts Cost      Prio.Nbr Type---------------- ---- --- --------- -------- --------------------------------Fa0/1            Root FWD 19        128.1    P2pFa0/2            Altn BLK 19        128.2    P2p&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s configure our switch:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Switch(config)#int range f 0/1-2Switch(config-if-range)#channel-group 1 mode passiveCreating a port-channel interface Port-channel 1%LINEPROTO-5-UPDOWN: Line protocol on Interface FastEthernet0/1, changed state to down%LINEPROTO-5-UPDOWN: Line protocol on Interface FastEthernet0/1, changed state to up%LINEPROTO-5-UPDOWN: Line protocol on Interface FastEthernet0/2, changed state to down%LINEPROTO-5-UPDOWN: Line protocol on Interface FastEthernet0/2, changed state to up%SYS-5-CONFIG_I: Configured from console by console%LINK-5-CHANGED: Interface Port-channel1, changed state to up%LINEPROTO-5-UPDOWN: Line protocol on Interface Port-channel1, changed state to up&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The console message inform us that the EtherChannel was created and that it is up.&lt;/p&gt;&lt;p&gt;Verify:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3#sh etherchannel summary ! ... snip ...Number of channel-groups in use: 1Number of aggregators:           1Group  Port-channel  Protocol    Ports------+-------------+-----------+----------------------------------------------1      Po1(SU)           LACP   Fa0/1(P) Fa0/2(P)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If this is indeed a trunk port, we ensure that trunking is enabled.&lt;/p&gt;&lt;p&gt;First, configure both ends:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3(config)#int port-channel 1L3(config-if)#switchport trunk encapsulation dot1qL3(config-if)#switchport mode trunk&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Switch(config)#int port-channel 1L3(config-if)#switchport mode trunk&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice that &lt;code&gt;switchport trunk encapsulation dot1q&lt;/code&gt; is not necessary (or evenavailable) on a 2940 switch.&lt;/p&gt;&lt;p&gt;Verify both ends:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;L3#sh int trunkPort        Mode         Encapsulation  Status        Native vlanPo1         on           802.1q         trunking      1 &lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Switch#sh int trunkPort        Mode         Encapsulation  Status        Native vlanPo1         on           802.1q         trunking      1&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;load-balancing&#34;&gt;Load Balancing&lt;/h2&gt;&lt;p&gt;The device needs a way to determine which link to use to send PDUs over. Itcould look at the destination MAC address for example. If we have a total of 2redundant links, it looks at the last bit of the MAC address. This could beeither a 0 or a 1. If the last bit is 0, it sends it over one link, if the bitis 1, it will send it over the other. We want these bits to be as random aspossible so that both links are utilized equally. We can do this by setting theload balancing algorithm, such as &lt;code&gt;src-dst-mac&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Router(config)#port-channel load-balance ?dst-ip       Dst IP Addrdst-mac      Dst Mac Addrsrc-dst-ip   Src XOR Dst IP Addrsrc-dst-mac  Src XOR Dst Mac Addrsrc-ip       Src IP Addrsrc-mac      Src Mac Addr&lt;/code&gt;&lt;/pre&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai spell: --&gt;</description>
     </item>
   
     <item>
       <title>Run SpinRite on Librem 5 Internal Storage</title>
       <link>https://relentlesscoding.com/posts/run-spinrite-on-librem-5-internal-storage/</link>
       <pubDate>Sun, 16 Feb 2025 13:34:50 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/run-spinrite-on-librem-5-internal-storage/</guid>
       <description>&lt;p&gt;I have a &lt;a href=&#34;https://puri.sm/products/librem-5/&#34;&gt;Purism Librem 5 Linux phone&lt;/a&gt;. The Evergreen batch has 32 GiB offlash storage. In this post I will show how to run SpinRite on that drive.&lt;/p&gt;&lt;!-- more --&gt;&lt;p&gt;Since this is a rather large post, a table of contents seems in order.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;#tools-used&#34;&gt;Tools Used&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#prerequisites&#34;&gt;Prerequisites&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#step-1-buy-spinrite&#34;&gt;Step 1: Buy SpinRite&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;#step-11-install-wine&#34;&gt;Step 1.1: Install Wine&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#step-12-extract-bootable-spinrite-iso-from-spinrite-windows-binary&#34;&gt;Step 1.2: Extract Bootable SpinRite ISO from SpinRite WindowsBinary&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#step-2-run-spinrite-in-virtualbox&#34;&gt;Step 2: Run SpinRite in VirtualBox&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;#step-21a-using-the-gui&#34;&gt;Step 2.1a: Using the GUI&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#step-21b-using-the-cli&#34;&gt;Step 2.1b: Using the CLI&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#step-22-start-the-virtual-machine&#34;&gt;Step 2.2: Start the VirtualMachine&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#step-3-use-jumpdrive-to-access-librem-5-internal-storage&#34;&gt;Step 3: Use Jumpdrive to Access Librem 5 InternalStorage&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;#step-31-install-jumpdrive-on-your-host-machine&#34;&gt;Step 3.1: Install Jumpdrive on Your HostMachine&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#step-32-put-librem-5-in-flash-mode&#34;&gt;Step 3.2: Put Librem 5 in FlashMode&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#step-33-start-librem-5-with-jumpdrive&#34;&gt;Step 3.3: Start Librem 5 withJumpdrive&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#step-4-allow-virtual-machine-access-to-raw-disk-drive&#34;&gt;Step 4: Allow Virtual Machine Access to Raw DiskDrive&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;#step-41-create-medium-from-raw-drive&#34;&gt;Step 4.1: Create Medium from RawDrive&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#step-42-attach-raw-disk-to-virtual-machine&#34;&gt;Step 4.2: Attach Raw Disk to VirtualMachine&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#step-5-finally-run-spinrite-on-the-drive&#34;&gt;Step 5: Finally, Run SpinRite on theDrive&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;tools-used&#34;&gt;Tools Used&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;A copy of SpinRite&lt;/li&gt;&lt;li&gt;VirtualBox (and &lt;a href=&#34;https://www.virtualbox.org/manual/topics/vboxmanage.html&#34;&gt;VBoxManage&lt;/a&gt; on the CLI)&lt;/li&gt;&lt;li&gt;Wine&lt;/li&gt;&lt;li&gt;&lt;code&gt;uuu&lt;/code&gt; from &lt;code&gt;mfgtools&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;&lt;p&gt;I use Arch Linux, so all commands and examples are run on that OS. You should beable to easily adjust the commands to your own Unix-based OS.&lt;/p&gt;&lt;p&gt;I have VirtualBox version 7.1.6r167084 installed. But none of the features usedin this post are recent by any measure, so I would not be surprised if olderversions, including 6, would work fine.&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Make sure you have the appropriate VirtualBox kernel modules installed andloaded. This means at least &lt;code&gt;vboxdrv&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;#&lt;/span&gt; modprobe vboxdrv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;step-1-buy-spinrite&#34;&gt;Step 1: Buy SpinRite&lt;/h2&gt;&lt;p&gt;&lt;a href=&#34;https://www.grc.com/cs/prepurch.htm&#34;&gt;It&amp;rsquo;s only $89&lt;/a&gt; for the world&amp;rsquo;s best mass storage data-recovery, repair,maintenance and performance-enhancing tool.&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;h3 id=&#34;step-11-install-wine&#34;&gt;Step 1.1: Install Wine&lt;/h3&gt;&lt;p&gt;To get the ISO image, we need to run the SpinRite Windows executable. To runthis executable under Linux, we need &lt;a href=&#34;https://wiki.archlinux.org/title/Wine&#34;&gt;Wine&lt;/a&gt;. To install Wine, first make sureyou have enabled the multilib repository in &lt;code&gt;/etc/pacman.conf&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-conf&#34; data-lang=&#34;conf&#34;&gt;[multilib]Include = /etc/pacman.d/mirrorlist&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Update the package cache and install Wine:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;#&lt;/span&gt; pacman -Sy wine&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;step-12-extract-bootable-spinrite-iso-from-spinrite-windows-binary&#34;&gt;Step 1.2: Extract Bootable SpinRite ISO from SpinRite Windows Binary&lt;/h3&gt;&lt;p&gt;Use Wine to run the SpinRite Windows executable:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; wine /path/to/spinrite.exe&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We are shown the main SpinRite installer window:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/spinrite-installer-main-screen.png&#34; alt=&#34;The SpinRite main installer screen with five buttons on the bottom: &amp;ldquo;Copy&amp;rdquo;,&amp;ldquo;Install SpinRite on USB&amp;rdquo;, &amp;ldquo;Create ISO or IMG File&amp;rdquo;, &amp;ldquo;Create Boot Diskette&amp;rdquo; and&amp;ldquo;Exit&amp;rdquo;&#34;&gt;&lt;/p&gt;&lt;p&gt;Click on &amp;ldquo;Create ISO or IMG File&amp;rdquo;:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/spinrite-create-boot-image-file.png&#34; alt=&#34;The SpinRite window that asks the user whether it wants to save an ISO file tohard disk&#34;&gt;&lt;/p&gt;&lt;p&gt;Click on &amp;ldquo;Save a Boot Image File&amp;rdquo;:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/spinrite-store-iso.png&#34; alt=&#34;An Arch Linux dialog asking the user to store the file SpinRite.iso in theuser&amp;rsquo;s Downloads folder&#34;&gt;&lt;/p&gt;&lt;p&gt;Save the ISO somewhere you will be able to find it.&lt;/p&gt;&lt;h2 id=&#34;step-2-run-spinrite-in-virtualbox&#34;&gt;Step 2: Run SpinRite in VirtualBox&lt;/h2&gt;&lt;p&gt;Unless you are a particularly fast typist or have all the steps scripted, theGUI is the fastest way to create a new VM. But I will show the steps andcommands for both the GUI and the CLI.&lt;/p&gt;&lt;h3 id=&#34;step-21a-using-the-gui&#34;&gt;Step 2.1a: Using the GUI&lt;/h3&gt;&lt;p&gt;Open VirtualBox. Create a new virtual machine by clicking on &amp;ldquo;New&amp;rdquo;. Give it aname of &amp;ldquo;SpinRite&amp;rdquo;. Select the stored ISO file under &amp;ldquo;ISO Image&amp;rdquo;. The type of OSshould be &amp;ldquo;Other&amp;rdquo; and the version &amp;ldquo;DOS&amp;rdquo;:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/spinrite-create-vm-name-os.png&#34; alt=&#34;The &amp;ldquo;Name and Operating System&amp;rdquo; section of the &amp;ldquo;Create Virtual Machine&amp;rdquo;wizard. It shows sections for putting in a name, the ISO image, the OS type andversion.&#34;&gt;&lt;/p&gt;&lt;p&gt;You&amp;rsquo;ll need minimal hardware to run SpinRite: choosing 1 CPU and 64 MB of RAM should suffice for version 6.1r4&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/spinrite-create-vm-hardware.png&#34; alt=&#34;The &amp;ldquo;Hardware&amp;rdquo; section of the &amp;ldquo;Create Virtual Machine&amp;rdquo; wizard. It shows aslider called &amp;ldquo;Base Memory&amp;rdquo; set to 64 MB and a slider called &amp;ldquo;Processors&amp;rdquo; set to1.&#34;&gt;&lt;/p&gt;&lt;p&gt;Optionally, you can add a virtual hard drive to store logs:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/spinrite-create-vm-hard-disk.png&#34; alt=&#34;The &amp;ldquo;Hard Disk&amp;rdquo; section of the &amp;ldquo;Create Virtual Machine&amp;rdquo; wizard. It shows threeradio buttons. The upper one, &amp;ldquo;Create a Virtual Hard Disk Now&amp;rdquo;, is selected. Itis set to create a hard disk file called &amp;ldquo;SpinRite.vdi&amp;rdquo; of 100 MB.&#34;&gt;&lt;/p&gt;&lt;h3 id=&#34;step-21b-using-the-cli&#34;&gt;Step 2.1b: Using the CLI&lt;/h3&gt;&lt;p&gt;Or you can run the following commands in your terminal.&lt;/p&gt;&lt;p&gt;First, &lt;a href=&#34;https://www.virtualbox.org/manual/topics/vboxmanage.html#vboxmanage-createvm&#34;&gt;create the VM&lt;/a&gt;. The &lt;code&gt;--register&lt;/code&gt; option registers the VM with yourinstallation of VirtualBox, instead of just creating an XML config file.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; VBoxManage createvm --name SpinRite --ostype DOS --register&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can choose to keep the defaults for memory (128 MB) and CPU (1) or &lt;a href=&#34;https://www.virtualbox.org/manual/topics/vboxmanage.html#vboxmanage-modifyvm&#34;&gt;modifythem&lt;/a&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ VBoxManage modifyvm SpinRite --memory &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; --cpus &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.virtualbox.org/manual/topics/vboxmanage.html#vboxmanage-storagectl&#34;&gt;Create a SATA controller&lt;/a&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ VBoxManage storagectl SpinRite &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --name &lt;span class=&#34;s1&#34;&gt;&amp;#39;SATA Controller&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --add sata&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.virtualbox.org/manual/topics/vboxmanage.html#vboxmanage-storageattach&#34;&gt;Attach&lt;/a&gt; the SpinRite ISO as a startup DVD:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ VBoxManage storageattach SpinRite &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --storagectl &lt;span class=&#34;s1&#34;&gt;&amp;#39;SATA Controller&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --port &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --type dvddrive &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --medium /path/to/SpinRite.iso&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Optionally, you can &lt;a href=&#34;https://www.virtualbox.org/manual/topics/vboxmanage.html#vboxmanage-createmedium&#34;&gt;create a virtual hard drive&lt;/a&gt; for storing logs. &lt;code&gt;--size&lt;/code&gt;is in megabytes:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ VBoxManage createmedium disk &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --filename /path/to/SpinRite.vdi &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --size &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --format VDI&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.virtualbox.org/manual/topics/vboxmanage.html#vboxmanage-storageattach&#34;&gt;Attach&lt;/a&gt; the virtual hard drive (on port 1 since 0 is already taken):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ VBoxManage storageattach SpinRite &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --storagectl &lt;span class=&#34;s1&#34;&gt;&amp;#39;SATA Controller&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --port &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --type hdd &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --medium /path/to/SpinRite.vdi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Check your work:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ VBoxManage showvminfo SpinRite&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;step-22-start-the-virtual-machine&#34;&gt;Step 2.2: Start the Virtual Machine&lt;/h3&gt;&lt;p&gt;Make sure you are satisfied with the settings&lt;sup id=&#34;fnref:5&#34;&gt;&lt;a href=&#34;#fn:5&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;5&lt;/a&gt;&lt;/sup&gt;. Then, start the VM:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; VBoxManage startvm SpinRite&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Waiting for VM &amp;#34;SpinRite&amp;#34; to power on...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;VM &amp;#34;SpinRite&amp;#34; has been successfully started.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should now see the SpinRite splash screen:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/spinrite-splash-screen.png&#34; alt=&#34;The SpinRite splash screen&#34;&gt;&lt;/p&gt;&lt;h2 id=&#34;step-3-use-jumpdrive-to-access-librem-5-internal-storage&#34;&gt;Step 3: Use Jumpdrive to Access Librem 5 Internal Storage&lt;/h2&gt;&lt;p&gt;These steps were taken from &lt;a href=&#34;https://docs.puri.sm/Hardware/Librem_5/Troubleshooting/Repairing_Broken_Installation.html#put-the-librem-5-in-flash-mode&#34;&gt;the official Librem 5 documentation&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&#34;step-31-install-jumpdrive-on-your-host-machine&#34;&gt;Step 3.1: Install Jumpdrive on Your Host Machine&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://source.puri.sm/sebastiankrzyszkowiak/Jumpdrive/-/jobs/404437/artifacts/file/purism-librem5.tar.xz&#34;&gt;Download the latest build of Jumpdrive&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Extract the tar ball:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mkdir jumpdrive&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; tar -C jumpdrive -xf purism-librem5.tar.xz&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://aur.archlinux.org/packages/mfgtools-git&#34;&gt;Install &lt;code&gt;uuu&lt;/code&gt; from the AUR&lt;/a&gt;&lt;sup id=&#34;fnref:6&#34;&gt;&lt;a href=&#34;#fn:6&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;6&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mkdir ~/aur&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; ~/aur&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; git clone -- &lt;span class=&#34;s1&#34;&gt;&amp;#39;https://aur.archlinux.org/mfgtools-git.git&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; makepkg -s&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; sudo pacman -U mfgtools-git-1.5.201.r3.gab8dbdf-1-x86_64.pkg.tar.zst&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By the time you read this, the version of &lt;code&gt;mfgtools-git&lt;/code&gt; will have changed.Adjust the &lt;code&gt;pacman -U&lt;/code&gt; command accordingly.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&#34;step-32-put-librem-5-in-flash-mode&#34;&gt;Step 3.2: Put Librem 5 in Flash Mode&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;Fully power off the Librem 5.&lt;/li&gt;&lt;li&gt;Turn off all hardware kill switches on the Librem 5 (move all to the &amp;ldquo;bottom&amp;rdquo;position toward the bottom of the phone).&lt;/li&gt;&lt;li&gt;Remove the battery from the Librem 5.&lt;/li&gt;&lt;li&gt;Plug in the USB cable to your Linux PC.&lt;/li&gt;&lt;li&gt;Hold the volume-up button on the Librem 5.&lt;/li&gt;&lt;li&gt;Plug in the USB cable to the Librem 5. The red light should blink, and youshould not see the green light.&lt;/li&gt;&lt;li&gt;Reinsert the Librem 5 battery. The red light will become solid.&lt;/li&gt;&lt;li&gt;Release the volume-up button on the Librem 5.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;If all of this seems hard, &lt;a href=&#34;https://docs.puri.sm/Hardware/Librem_5/Maintenance/Reflashing.html&#34;&gt;watch the video below&lt;/a&gt;:&lt;/p&gt;&lt;video controls width=&#34;100%&#34;&gt;    &lt;source src=&#34;https://videos.puri.sm/promo/reflashing.mp4#t=55&#34; type=&#34;video/mp4&#34;&gt;    &lt;source src=&#34;https://videos.puri.sm/promo/reflashing.webm#t=55&#34; type=&#34;video/webm&#34;&gt;    Your browser does not support HTML video.&lt;/video&gt;&lt;p&gt;Your host Linux should recognize that a USB device has been attached.&lt;/p&gt;&lt;h3 id=&#34;step-33-start-librem-5-with-jumpdrive&#34;&gt;Step 3.3: Start Librem 5 with Jumpdrive&lt;/h3&gt;&lt;p&gt;Now, for the final step, execute:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; ./boot-purism-librem5.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After a couple of seconds, you should see &amp;ldquo;Jumpdrive is running&amp;rdquo; on your phone.If you run &lt;code&gt;lsblk&lt;/code&gt;, you should see the Librem 5 listed as a block storagedevice.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; lsblk&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;NAME          MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;sda             8:0    0 238.5G  0 disk  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;├─sda1          8:1    0   500M  0 part  /boot&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;├─sda2          8:2    0     4G  0 part  [SWAP]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;└─sda3          8:3    0   234G  0 part  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  └─cryptroot 254:0    0   234G  0 crypt /&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;sdb             8:16   1  29.1G  0 disk  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;├─sdb1          8:17   1   465M  0 part  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;└─sdb2          8:18   1  28.7G  0 part  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;sdc             8:32   1 119.3G  0 disk&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In my case, the Librem 5 internal storage is available on &lt;code&gt;/dev/sdb&lt;/code&gt;. How do Iknow this? I looked at the SIZE of the drive. Peruse the output of &lt;code&gt;fdisk -l&lt;/code&gt; ifyou are uncertain.&lt;/p&gt;&lt;h2 id=&#34;step-4-allow-virtual-machine-access-to-raw-disk-drive&#34;&gt;Step 4: Allow Virtual Machine Access to Raw Disk Drive&lt;/h2&gt;&lt;p&gt;Now, we need to make sure VirtualBox can work with the block device. We willcreate a disk image that points directly to the raw device.&lt;/p&gt;&lt;h3 id=&#34;step-41-create-medium-from-raw-drive&#34;&gt;Step 4.1: Create Medium from Raw Drive&lt;/h3&gt;&lt;p&gt;From &lt;code&gt;lsblk&lt;/code&gt;, we know the Librem 5 internal storage is available at &lt;code&gt;/dev/sdb&lt;/code&gt;.SpinRite works on entire devices, so I will be ignoring partitions.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ vboxmanage createmedium disk &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --filename&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/path/to/l5flash &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --format&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;VMDK &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --variant&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;RawDisk &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --property &lt;span class=&#34;nv&#34;&gt;RawDrive&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/dev/sdb&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This creates a virtual disk image called &lt;code&gt;l5flash.vmdk&lt;/code&gt; in the specified path.SpinRite needs access to the raw device, so we specify &lt;code&gt;--variant=RawDisk&lt;/code&gt; andwhere it can be found.&lt;/p&gt;&lt;p&gt;The output is:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%Medium created. UUID: 541a980e-e201-44af-a927-6f3d83925bb0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We remember the UUID of the freshly-created medium.&lt;/p&gt;&lt;h3 id=&#34;step-42-attach-raw-disk-to-virtual-machine&#34;&gt;Step 4.2: Attach Raw Disk to Virtual Machine&lt;/h3&gt;&lt;p&gt;Attach the raw-disk medium to the virtual machine. Choose an empty port. If youdo not know, check &lt;code&gt;VBoxManage showvminfo SpinRite&lt;/code&gt; to see which ports areavailable. In our example, ports 0 (SpinRite.iso) and 1 (logs) are alreadyoccupied, so we pick 2:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ vboxmanage storageattach SpinRite &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --storagectl &lt;span class=&#34;s1&#34;&gt;&amp;#39;SATA Controller&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --port &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --type hdd &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --medium 541a980e-e201-44af-a927-6f3d83925bb0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Set &lt;code&gt;--medium none&lt;/code&gt; if you want to detach the drive again later.&lt;/p&gt;&lt;h2 id=&#34;step-5-finally-run-spinrite-on-the-drive&#34;&gt;Step 5: Finally, Run SpinRite on the Drive&lt;/h2&gt;&lt;p&gt;After starting the VM, the drive should show in SpinRite:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/spinrite-librem5-flash-drive.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&lt;hr&gt;&lt;ol&gt;&lt;li id=&#34;fn:1&#34;&gt;&lt;p&gt;Incidentally, you can create a TOC from a Markdown document using&lt;code&gt;pandoc&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ pandoc -f markdown -t markdown -s --toc doc.md &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This creates a new Markdown document that starts with a TOC. You can yank itand paste it in the original document. Make sure it makes sense first,though.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:2&#34;&gt;&lt;p&gt;But if it doesn&amp;rsquo;t, &lt;a href=&#34;https://relentlesscoding.com/pgp/public-key&#34;&gt;do let me know&lt;/a&gt;.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:3&#34;&gt;&lt;p&gt;But you know that, because you listen to &lt;a href=&#34;https://www.grc.com/securitynow.htm&#34;&gt;Security Now!&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:4&#34;&gt;&lt;p&gt;After executing SpinRite, a memory test is run. The accompanying texttells us it uses over 50 MB of RAM:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/spinrite-testing-system-ram.png&#34; alt=&#34;Shows the &amp;ldquo;Testing System RAM&amp;rdquo; SpinRite screen. There is text visible thatexplains what this does.&#34;&gt;&lt;/p&gt;&lt;p&gt;Taking into account the memory overhead of FreeDOS (640KB), you should begood with 64 MB.&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:5&#34;&gt;&lt;p&gt;Personally, I like to disable things I do not use. These include NIC,audio and serial ports:&lt;/p&gt;&lt;p&gt;Disable network adapter 1 (and any others if present):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ VBoxManage modifyvm SpinRite --nic1 none&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Disable audio:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ VBoxManage modifyvm SpinRite --audio-enabled off&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Disable serial port COM1 (and any others if enabled)&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ VBoxManage modifyvm SpinRite --uart1 off&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&amp;#160;&lt;a href=&#34;#fnref:5&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/li&gt;&lt;li id=&#34;fn:6&#34;&gt;&lt;p&gt;On PureOS and possibly all Debian-based systems it might be in thedefault repositories: &lt;code&gt;apt install uuu&lt;/code&gt;.&amp;#160;&lt;a href=&#34;#fnref:6&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Convert Linux into NAT Router</title>
       <link>https://relentlesscoding.com/posts/convert-linux-into-nat-router/</link>
       <pubDate>Sat, 15 Feb 2025 08:53:51 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/convert-linux-into-nat-router/</guid>
       <description>&lt;p&gt;Sometimes it comes in handy to have a Linux box act as a router. You might nothave a switch lying around. You might want to monitor all traffic coming andgoing to some device. In this post, we will set up Linux to act as a simple IPv4NAT router.&lt;/p&gt;&lt;!-- more --&gt;&lt;p&gt;We will call the Linux machine &lt;code&gt;linux&lt;/code&gt; and the machine using the Linux box forinternet breakout &lt;code&gt;remote&lt;/code&gt;.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;You need 2 NICs on your Linux. They can be 2 ethernet ports. But you couldalso use your wireless interface for this. In this post, we assume they arecabled connections called &lt;code&gt;eth0&lt;/code&gt; and &lt;code&gt;eth1&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Connect &lt;code&gt;eth0&lt;/code&gt; to the gateway.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Connect the two machines with an ethernet cable. The cable in the Linuxmachine should go in &lt;code&gt;eth1&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Make sure the interfaces on both sides are up:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;linux# ip link &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; eth1 up&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote# ip link &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; eth0 up&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Reminder: you can abbreviate these commands to &lt;code&gt;ip l s eth&amp;lt;n&amp;gt; up&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Assign both ends &lt;a href=&#34;https://www.rfc-editor.org/rfc/rfc1918.html&#34;&gt;an RFC 1918 private IPv4 address&lt;/a&gt;. You could use&lt;a href=&#34;https://en.wikipedia.org/wiki/Link-local_address&#34;&gt;link-local addresses (APIPA)&lt;/a&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;linux# ip addr add 169.254.0.1/32 peer 169.254.0.2 dev eth1&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote# ip addr add 169.254.0.2/32 peer 169.254.0.1 dev eth0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Shorter: &lt;code&gt;ip a a 169.254.0.1/32 peer 169.254.0.2 dev eth1&lt;/code&gt;&lt;/p&gt;&lt;p&gt;I configure the addresses to be point-to-point, because there are only 2hosts on this network and they are directly connected.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Enable IPv4 forwarding in the kernel on the Linux router&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;linux# sysctl -w net.ipv4.ip_forward&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Enable NAT on the Linux router.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;linux# iptables -t nat -A POSTROUTING &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -s 169.254.0.0/16 &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -o eth0 &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -j MASQUERADE&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will rewrite the source address of the packets with the original sourceaddress in the &lt;code&gt;169.254/16&lt;/code&gt; network to the primary source address of theoutgoing interface &lt;code&gt;eth0&lt;/code&gt;. The &lt;code&gt;MASQUERADE&lt;/code&gt; target is easiest to use, but:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;It should only be used with dynamically assigned IP (dialup) connections:if you have a static IP address, you should use the &lt;code&gt;SNAT&lt;/code&gt; target.Masquerading is equivalent to specifying a mapping to the IP address of theinterface the packet is going out, but also has the effect that connectionsare forgotten when the interface goes down. This is the correct behaviorwhen the next dialup is unlikely to have the same interface address (andhence any established connections are lost anyway).&lt;/p&gt;&lt;p&gt;&amp;ndash;&lt;cite&gt;&lt;a href=&#34;https://man.archlinux.org/man/core/iptables/iptables-extensions.8.en#MASQUERADE&#34;&gt;iptables-extensions(8)&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The alternative is to use the &lt;code&gt;SNAT&lt;/code&gt; target with a static (private) IPv4address. It should be an address configured on the interface which you eitherconfigured statically yourself, or you got from a DHCP server. You can checkwith &lt;code&gt;ip -br addr show eth0&lt;/code&gt;. Here, I am using &lt;code&gt;192.168.1.123&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;linux# iptables -t nat -A POSTROUTING &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -s 169.254.0.0/16 &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -o eth0 &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -j SNAT --to-source 192.168.1.123&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;If you have a strict firewall on your Linux router, you might have set&lt;code&gt;iptables -P FORWARD DROP&lt;/code&gt;. You need to allow traffic to flow between the 2NICs. For example:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;linux# iptables -I FORWARD -i eth1 -o eth0 -j ACCEPT&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;linux# iptables -I FORWARD -i eth0 -o eth1 -j ACCEPT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Finally, make the Linux router the default gateway of the dependent box:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remote# ip route add default via 169.254.0.1 dev eth0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can abbreviate &lt;code&gt;route add&lt;/code&gt; to &lt;code&gt;r a&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&#34;a-note-on-interface-names&#34;&gt;A Note on Interface Names&lt;/h2&gt;&lt;p&gt;If you have complicated setup with virtual interfaces (VPN, VLAN) and a firewallin place, add logging to your firewall rules when packets get dropped, so youknow which interfaces to use in your source NAT (&lt;code&gt;-t nat -A POSTROUTING&lt;/code&gt;) and&lt;code&gt;FORWARD&lt;/code&gt; rules.&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;</description>
     </item>
   
     <item>
       <title>Create Server Backup from Local Machine with Borg</title>
       <link>https://relentlesscoding.com/posts/create-server-backup-from-local-machine/</link>
       <pubDate>Fri, 14 Feb 2025 16:36:03 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/create-server-backup-from-local-machine/</guid>
       <description>&lt;p&gt;How to back up your servers with borg? One way to do it, as &lt;a href=&#34;https://borgbackup.readthedocs.io/en/stable/deployment/pull-backup.html#sshfs&#34;&gt;documented by thefine borg documentation&lt;/a&gt;, is to use a pull backup using &lt;a href=&#34;https://man.archlinux.org/man/extra/sshfs/sshfs.1.en&#34;&gt;sshfs&lt;/a&gt;. The ideais to mount the remote filesystem in a local directory, chroot into it, and runborg inside the chroot.&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;why-the-chroot&#34;&gt;Why the chroot?&lt;/h2&gt;&lt;p&gt;Borg stores the owner and group. Only the client (= system that gets backed up)can map from UID/GID to user and group name by looking at &lt;code&gt;/etc/passwd&lt;/code&gt;. If wedo not chroot, then it will use the &lt;code&gt;/etc/passwd&lt;/code&gt; of the backup server. You canbypass all this by just &lt;a href=&#34;https://borgbackup.readthedocs.io/en/stable/usage/create.html&#34;&gt;storing the numeric IDs&lt;/a&gt; always with &lt;code&gt;borg create --numeric-ids&lt;/code&gt;&lt;/p&gt;&lt;h2 id=&#34;create-backup-from-remote-filesystem-using-pull&#34;&gt;Create Backup From Remote Filesystem Using Pull&lt;/h2&gt;&lt;p&gt;Make sure the borg repo is &lt;code&gt;cryptsetup&lt;/code&gt;ed and &lt;code&gt;mount&lt;/code&gt;ed.&lt;/p&gt;&lt;h2 id=&#34;create-a-restricted-root-user-on-the-remote-system&#34;&gt;Create a restricted root user on the remote system&lt;/h2&gt;&lt;p&gt;On Debian, I need to create the &lt;code&gt;/run/sshd&lt;/code&gt; directory first when I try to run&lt;code&gt;sshd&lt;/code&gt; with a custom &lt;code&gt;sshd_config&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# /usr/sbin/sshd -D -f /etc/ssh/sshd_config.p2p&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Normally, Debian uses inetd-style Systemd sockets (see&lt;a href=&#34;https://man.archlinux.org/man/systemd.socket.5&#34;&gt;&lt;code&gt;systemd.socket(5)&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;The &lt;a href=&#34;https://man.archlinux.org/man/core/openssh/sshd_config.5.en&#34;&gt;&lt;code&gt;sshd_config(5)&lt;/code&gt;&lt;/a&gt; needs to contain:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;PermitRootLogin forced-commands-onlyAllowUsers root&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Be careful to set &lt;code&gt;PermitRootLogin&lt;/code&gt; to &lt;code&gt;forced-commands-only&lt;/code&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;If this option is set to &lt;code&gt;forced-commands-only&lt;/code&gt;, root login with public keyauthentication will be allowed, but only if the command option has beenspecified (which may be useful for taking remote backups even if root login isnormally not allowed). All other authentication methods are disabled for root.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;It is questionable security to allow password authentication over SSH, so let&amp;rsquo;sdisable that in &lt;code&gt;sshd_config&lt;/code&gt; as well&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;PasswordAuthentication no&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since we do not want to use passwords for SSH access, we create an SSH key:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ C=&amp;#34;$(hostname)-$USER-remotehost-remoteuser-$(date -I)&amp;#34;$ ssh-keygen -t ed25519 -f ~/.ssh/&amp;#34;$C&amp;#34; -C &amp;#34;$C&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Copy the public key to the remote host and put it in&lt;code&gt;/root/.ssh/authorized_keys&lt;/code&gt;. Let&amp;rsquo;s also put some restrictions on this key:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;restrict,command=&amp;#34;internal-sftp&amp;#34; ssh-ed25519 AAA...&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;restrict&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Enable all restrictions, i.e. disable port, agent and X11 forwarding, aswell as  disabling PTY  allocation  and  execution  of ~/.ssh/rc.  If anyfuture restriction capabilities are added to &lt;code&gt;authorized_keys&lt;/code&gt; files, theywill be included in this set.&lt;/p&gt;&lt;p&gt;&amp;ndash;&lt;cite&gt;&lt;a href=&#34;https://man.archlinux.org/man/core/openssh/sshd.8.en#restrict&#34;&gt;sshd(8)&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;code&gt;command&lt;/code&gt; enforces the given command (&lt;code&gt;internal-sftp&lt;/code&gt;), even if the userprovided another command.&lt;/p&gt;&lt;h2 id=&#34;unlocking-the-root-user&#34;&gt;Unlocking the Root User&lt;/h2&gt;&lt;p&gt;The root user might be locked by default. Locked means the user account cannotbe used. You cannot use a Unix password to log in, nor use any other means toaccess the account. Unlock root by setting the account expiry date to -1 (&lt;code&gt;-e -1&lt;/code&gt; = no expiry). root also needs a valid default login shell (see&lt;code&gt;/etc/shells&lt;/code&gt;; using &lt;code&gt;-s &amp;lt;shell&amp;gt;&lt;/code&gt; sets the shell):&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# usermod -e -1 -s /bin/sh root&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It is not necessary to set a password for the account nor to unlock the passwordsince we will be using SSH keys to authenticate, not passwords.&lt;/p&gt;&lt;p&gt;It might be a good idea to lock the account again after the backup is done:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# usermod -L -e 1 -s /usr/bin/nologin root&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This disables password login, expires the account and changes the user shell tothe &lt;code&gt;nologin&lt;/code&gt; program, which prints an error message and exits.&lt;/p&gt;&lt;h2 id=&#34;make-actual-backup&#34;&gt;Make Actual Backup&lt;/h2&gt;&lt;p&gt;Mount the remote filesystem:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mkdir /tmp/sshfs$ sshfs \    -o IdentifyFile=~/.ssh/&amp;lt;privkey&amp;gt; \    -p 12345 \    -o allow_other \    -o reconnect \    -o dir_cache=yes \    root@remote:/ /tmp/sshfs&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;-o IdentityFile&lt;/code&gt; should point to the file containing the private key we createdearlier.&lt;/p&gt;&lt;p&gt;We need &lt;code&gt;-o allow_other&lt;/code&gt; for the root user to bind mount the borg repo into thesshfs. &lt;code&gt;-o allow_other&lt;/code&gt; is documented in &lt;a href=&#34;https://man.archlinux.org/man/mount.fuse.8&#34;&gt;&lt;code&gt;mount.fuse(5)&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;allow_other&lt;/code&gt;This option overrides the security measure restricting file access to theuser mounting the filesystem. So all users (including root) can access thefiles. This option is by default only allowed to root, but thisrestriction can be removed with a configuration option described in theprevious section.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Some other options to consider (from &lt;a href=&#34;https://man.archlinux.org/man/extra/sshfs/sshfs.1.en&#34;&gt;&lt;code&gt;sshfs(1)&lt;/code&gt;&lt;/a&gt;):&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;-o reconnect&lt;/code&gt;automatically reconnect to server if connection is interrupted. Attemptsto access files that were opened before the reconnection will give errorsand need to be re-opened.&lt;/p&gt;&lt;p&gt;&lt;code&gt;-o dir_cache=BOOL&lt;/code&gt;Enables (yes) or disables (no) the SSHFS directory cache. The directorycache holds the names of directory entries. Enabling it allows readdir(3)system calls to be processed without network access.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Mount the borg repository inside it.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mkdir sshfs/mnt/borg/borgrepo# mount --bind /mnt/borg/borgrepo sshfs/mnt/borg/borgrepo&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Install borg on the remote machine. Or &lt;a href=&#34;https://borgbackup.readthedocs.io/en/stable/installation.html#standalone-binary&#34;&gt;download the borg standalone binary&lt;/a&gt;(&lt;a href=&#34;https://borg.bauerj.eu/&#34;&gt;the ARM version&lt;/a&gt;) and copy it on the remote machine. Do not forget to makeit executable:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cp /path/to/standalone/borg /tmp/sshfs/usr/local/bin/borg$ chmod u+x /tmp/sshfs/usr/local/bin/borg&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://wiki.archlinux.org/title/Chroot#Using_chroot&#34;&gt;Mount &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;proc&lt;/code&gt; and &lt;code&gt;sys&lt;/code&gt;&lt;/a&gt; inside the sshfs&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cd /tmp/sshfs$ for fs in dev proc sys; do sudo mount --bind /$fs $fs; done&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Mount borg config and cache directories:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mkdir -p /tmp/sshfs/root/.config/borg$ chmod 0700 /tmp/sshfs/root/.config/borg$ sudo mount --bind /root/.config/borg /tmp/sshfs/root/.config/borg$ mkdir -p /tmp/sshfs/root/.cache/borg$ chmod 0700 /tmp/sshfs/root/.cache/borg$ sudo mount --bind /root/.cache/borg /tmp/sshfs/root/.cache/borg&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, enter the chroot:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# chroot /tmp/sshfs&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;things-to-pay-attention-to&#34;&gt;Things to Pay Attention to&lt;/h2&gt;&lt;h3 id=&#34;do-not-back-up-borg-repository&#34;&gt;Do Not Back Up borg Repository&lt;/h3&gt;&lt;p&gt;When running &lt;code&gt;borg create&lt;/code&gt;, make sure to &lt;code&gt;--exclude /mnt/borg/borgrepo&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&#34;do-not-use-inode-numbers-for-borg-caching&#34;&gt;Do Not Use Inode Numbers for borg Caching&lt;/h3&gt;&lt;p&gt;Set &lt;code&gt;--files-cache ctime,size&lt;/code&gt;. See &lt;a href=&#34;https://man.archlinux.org/man/borg-create.1#files&#34;&gt;&lt;code&gt;borg-create(1)&lt;/code&gt;&lt;/a&gt;. The reason is that,by default, borg uses &lt;code&gt;ctime,size,inode&lt;/code&gt; to determine whether a file has beenchanged. But sshfs cannot guarantee stable &lt;a href=&#34;https://en.wikipedia.org/wiki/Inode#Details&#34;&gt;inode numbers&lt;/a&gt;. If the inodenumbers are not stable, the cache is useless: all files look like they havechanged from invocation to invocation.&lt;/p&gt;&lt;h3 id=&#34;provide-borg-passphrase-over-anonymous-pipe&#34;&gt;Provide borg Passphrase over Anonymous Pipe&lt;/h3&gt;&lt;p&gt;If you do not want to type in your credentials when prompted, you can pass inyour credentials with &lt;code&gt;pass&lt;/code&gt; (or another program that outputs the secret over ananonymous pipe):&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# pass show borg-secret | \    chroot /tmp/sshfs \    BORG_PASSPHRASE_FD=0 borg create repo::archive /home /etc&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;BORG_PASSPHRASE_FD&lt;/code&gt; takes a file descriptor &lt;em&gt;number&lt;/em&gt;. &lt;code&gt;0&lt;/code&gt; is stdin. See&lt;a href=&#34;https://man.archlinux.org/man/extra/borg/borg.1.en#BORG_PASSPHRASE_FD&#34;&gt;&lt;code&gt;borg(1)&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&#34;tear-down&#34;&gt;Tear Down&lt;/h2&gt;&lt;p&gt;After the backup is done, exit the chroot and unmount all mounted filesystemsrecursively:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;chroot# exitlocal# umount -R /tmp/sshfs&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;this-was-tested-on-the-following-versions&#34;&gt;This Was Tested on the Following Versions&lt;/h2&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ borg --versionborg 1.4.0$ sshfs --versionSSHFS version 3.7.3FUSE library version 3.16.2using FUSE kernel interface version 7.38fusermount3 version: 3.16.2&lt;/code&gt;&lt;/pre&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&lt;hr&gt;&lt;ol&gt;&lt;li id=&#34;fn:1&#34;&gt;&lt;p&gt;This means that even when you accidentally set &lt;code&gt;PermitRootLogin&lt;/code&gt; to &lt;code&gt;yes&lt;/code&gt;,you still cannot log in using a password, because &lt;code&gt;PasswordAuthentication&lt;/code&gt;takes precedence.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:2&#34;&gt;&lt;p&gt;Why mount &lt;code&gt;/dev/&lt;/code&gt;, &lt;code&gt;/proc&lt;/code&gt; and &lt;code&gt;/sys&lt;/code&gt;?&lt;code&gt; &lt;/code&gt;/dev&lt;code&gt; provides device files that are essential for programs inside the chroot to interact with hardware, such as terminals, disks, and other hardware resources. See also [&lt;/code&gt;hier(7)`]&lt;a href=&#34;https://man.archlinux.org/man/hier.7&#34;&gt;12&lt;/a&gt; and &lt;a href=&#34;https://en.wikipedia.org/wiki/Device_file&#34;&gt;the Wikipedia entry ondevice files&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;code&gt;/proc&lt;/code&gt; is a virtual filesystem that provides information about runningprocesses and the kernel. Programs use &lt;code&gt;/proc&lt;/code&gt; to gather system information,monitor processes, and manage resources. See also &lt;a href=&#34;https://man.archlinux.org/man/hier.7&#34;&gt;&lt;code&gt;hier(7)&lt;/code&gt;&lt;/a&gt;,&lt;a href=&#34;https://man.archlinux.org/man/proc.5.en&#34;&gt;&lt;code&gt;proc(5)&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;https://en.wikipedia.org/wiki/Procfs&#34;&gt;the Wikipedia entry on procfs&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;code&gt;/sys&lt;/code&gt; provides an interface to the kernel&amp;rsquo;s internal data structures. Itallows programs to access and modify kernel parameters, device drivers, andother system settings. Mounting &lt;code&gt;/sys&lt;/code&gt; enables programs within the chroot tointeract with the kernel. See also &lt;a href=&#34;https://man.archlinux.org/man/hier.7&#34;&gt;&lt;code&gt;hier(7)&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;https://en.wikipedia.org/wiki/Sysfs&#34;&gt;the Wikipedia entryon sysfs&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;But this is all theory. If you do not mount these filesystems, borg willstill run fine. Maybe I will dive into this in another blog post: how do youknow what you need in a chroot?&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Updating My Cisco 4321 Router&#39;s IOS over HTTP, FTP and TFTP and More</title>
       <link>https://relentlesscoding.com/posts/cisco-ios/</link>
       <pubDate>Wed, 12 Feb 2025 16:38:28 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/cisco-ios/</guid>
       <description>&lt;p&gt;I recently got my hands on a second-hand Cisco 4321 router. First things first,I want to upgrade to the latest IOS version so I have the latest securitypatches. Having no experience at all with IOS, these are some things I learnedalong the way.&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;conventions-used-in-this-post&#34;&gt;Conventions Used in This Post&lt;/h2&gt;&lt;p&gt;&lt;code&gt;Router&amp;gt;&lt;/code&gt; is a command prompt that indicates the user in currently in EXEC mode.&lt;code&gt;Router#&lt;/code&gt; indicates that the user is in privileged EXEC mode. You get from EXECto privileged EXEC mode by:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1&amp;gt;enableR1#disableR1&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Depending on your setup, you might need to type a password after &lt;code&gt;enable&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Every owner of Cisco equipment knows this, but I am one of my own futurereaders, so I need to keep things simple.&lt;/p&gt;&lt;h2 id=&#34;what-version-do-i-currently-run&#34;&gt;What Version Do I Currently Run?&lt;/h2&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1&amp;gt;sh version&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That shows:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Cisco IOS XE Software, Version 16.06.04Cisco IOS Software [Everest], ISR Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.6.4, RELEASE SOFTWARE (fc3)...System returned to ROM by Reload CommandSystem image file is &amp;#34;bootflash:isr4300-universalk9.16.06.04.SPA.bin&amp;#34;Last reload reason: Reload Command...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I am running IOS XE 16.06.04 and it got loaded from the file&lt;code&gt;isr4300-universalk9.16.06.04.SPA.bin&lt;/code&gt; stored on the &lt;code&gt;bootflash&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;finding-an-appropriate-ios-image&#34;&gt;Finding an Appropriate IOS Image&lt;/h2&gt;&lt;p&gt;Turns out Cisco are &lt;del&gt;dicks&lt;/del&gt; difficult about updates. You need a service contractbefore you can get a download link.&lt;/p&gt;&lt;p&gt;You can &lt;a href=&#34;https://www.cisco.com/c/en/us/support/index.html&#34;&gt;find your router model&lt;/a&gt; and navigate to the downloads tab. If youhave a support contract, you can log in and presumably download the latestversion. Also, Cisco hints that some older version &lt;em&gt;might be&lt;/em&gt; available withouta contract. But why would you want an old version with lots of security problemsto run on an internet-facing router, right?&lt;/p&gt;&lt;p&gt;I do not have a service contract. I did notice the previous owner had a newerversion of the firmware on the router&amp;rsquo;s flash memory:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#dir :flash...18  -rw-  763905745  Nov 9 2023 15:45:02 +00:00 isr4300-universalk9.17.09.04.SPA.bin...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Also, it looks like sometimes an IOS release falls off a truck. Search &lt;a href=&#34;kagi.com&#34;&gt;yourfavorite search engine&lt;/a&gt; for something like &lt;code&gt;intitle:index.of &amp;lt;image-name&amp;gt;&lt;/code&gt; to get a sense of where these trucks drive.&lt;/p&gt;&lt;h2 id=&#34;making-sure-you-did-not-just-download-malware&#34;&gt;Making Sure You Did Not Just Download Malware&lt;/h2&gt;&lt;p&gt;How do I know the firmware I got was not tampered with? Get the MD5 and SHA512checksums from the &lt;a href=&#34;https://software.cisco.com/download/home/286006221/type/282046477/release/Dublin-17.12.4a&#34;&gt;Cisco Software Download page&lt;/a&gt; when hovering over thefilename:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/cisco-ios-image-checksums.png&#34; alt=&#34;Cisco Software Download page shows image checksums when hovering over the filename&#34;&gt;&lt;/p&gt;&lt;p&gt;At least they did not hide &lt;em&gt;that&lt;/em&gt; information.&lt;/p&gt;&lt;p&gt;Now, let&amp;rsquo;s verify if the checksum on my router equals to what was published. MD5is old and broken, so let&amp;rsquo;s go with SHA512:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#verify /sha512 flash:isr4300-universalk9.17.09.04.SPA.bin 34f8eff288bb485c8ca902d2e5c03f9d45a4dc881e919a6ae3f0ca3a52161eecc5c9fc7a7ed90423984ee690695293ad011590fa0e75c65ccf003f7f89b0d5c0........................................................ &amp;lt;snip&amp;gt; ...Done!Verified (bootflash:isr4300-universalk9.17.09.04.SPA.bin) = 34f8eff288bb485c8ca902d2e5c03f9d45a4dc881e919a6ae3f0ca3a52161eecc5c9fc7a7ed90423984ee690695293ad011590fa0e75c65ccf003f7f89b0d5c0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;verify /sha512 flash:&amp;lt;file&amp;gt;&lt;/code&gt; calculates the checksum for the specified file. Ifyou provide a checksum yourself with &lt;code&gt;verify /sha512 flash:&amp;lt;file&amp;gt; &amp;lt;checksum&amp;gt;&lt;/code&gt;,the checksum is verified against your input. In this case, the checksum matches.Sweet!&lt;/p&gt;&lt;p&gt;Looking at the image name, I can also see that it contains &lt;code&gt;SPA&lt;/code&gt;. &lt;a href=&#34;https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/sys-image-mgmt/configuration/xe-16-8/sysimgmgmt-xe-16-8-book/sysimgmgmt-dgtly-sgnd-sw.html&#34;&gt;According toCisco&lt;/a&gt;, that stands for an image &lt;strong&gt;S&lt;/strong&gt;igned with key version &lt;strong&gt;A&lt;/strong&gt; meant for&lt;strong&gt;P&lt;/strong&gt;roduction use. The file can be verified as follows:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#verify flash:isr4300-universalk9.17.09.04.SPA.binVerifying file integrity of bootflash:isr4300-universalk9.17.12.04a.SPA.bin...Embedded Hash   SHA1 : 03A19E90D4E58FEDCAC30959192B8CEEDF6D29CCComputed Hash   SHA1 : 03A19E90D4E58FEDCAC30959192B8CEEDF6D29CCStarting image verificationHash Computation:    100%Done!Computed Hash   SHA2: 975953771aac683df79da71226460ecb                      fead063cf39b9b4f4165e8581dee89fd                      733868cc148e407e76ace6c2f912dac9                      fb7c95bdbd402b3fffa257c9ba6ebbd4                      Embedded Hash   SHA2: 975953771aac683df79da71226460ecb                      fead063cf39b9b4f4165e8581dee89fd                      733868cc148e407e76ace6c2f912dac9                      fb7c95bdbd402b3fffa257c9ba6ebbd4                      Digital signature successfully verified in file bootflash:isr4300-universalk9.17.12.04a.SPA.bin&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then there is the following command that:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Displays software authenticity-related information for the current ROMmon andthe Cisco IOS image file used for booting.&lt;/p&gt;&lt;/blockquote&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#sh software authenticity running SYSTEM IMAGE                                ------------                          Image type                    : Production      Signer Information                          Common Name           : CiscoSystems        Organization Unit     : IOS-XE              Organization Name     : CiscoSystems    Certificate Serial Number : 675E97D5    Hash Algorithm            : SHA512    Signature Algorithm       : 2048-bit RSA    Key Version               : A                                                              ROMMON------... snip ...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can view more information about the public keys used to verify the signedimages:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#show software authenticity keys&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point, I am not sure where my router got these public keys from, so I amhappy to just verify the checksum of the firmware image I am downloading on therouter. But good to know that there exist more robust ways to verify thefirmware actually came from Cisco.&lt;/p&gt;&lt;h2 id=&#34;what-transfer-methods-are-available&#34;&gt;What Transfer Methods Are Available?&lt;/h2&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1&amp;gt;enableR1#copy ?  /erase          Erase destination file system.  /error          Allow to copy error file.  /noverify       Don&amp;#39;t verify image signature before reload.  /verify         Verify image signature before reload.  bootflash:      Copy from bootflash: file system  cns:            Copy from cns: file system  flash:          Copy from flash: file system  ftp:            Copy from ftp: file system  http:           Copy from http: file system  https:          Copy from https: file system  null:           Copy from null: file system  nvram:          Copy from nvram: file system  rcp:            Copy from rcp: file system  running-config  Copy from current system configuration  scp:            Copy from scp: file system  startup-config  Copy from startup configuration  system:         Copy from system: file system  tar:            Copy from tar: file system  tftp:           Copy from tftp: file system  tmpsys:         Copy from tmpsys: file system  usb0:           Copy from usb0: file system  webui:          Copy from webui: file system&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Most of these options look familiar, but some I have never heard of, such as&lt;code&gt;cns:&lt;/code&gt; and &lt;code&gt;webui:&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;sneakernet-usb&#34;&gt;Sneakernet (USB)&lt;/h2&gt;&lt;p&gt;My router has a USB A port. When I plug in a USB drive (single partitionformatted as FAT32), it shows up under &lt;code&gt;usb0&lt;/code&gt;. Copy the image to flash memory:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#copy usb0:image.bin flash:&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But you might not always have a USB drive handy (or a USB port for that matter),so let&amp;rsquo;s look at some other ways to transfer files.&lt;/p&gt;&lt;h2 id=&#34;using-ip&#34;&gt;Using IP&lt;/h2&gt;&lt;p&gt;Transfer methods using IP should first have IP configured on the router:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1&amp;gt;enableR1#conf tR1(config)#int g0/0/0R1(config-if)#no shutR1(config-if)#ip addr 10.0.0.1 255.255.255.252R1(config-if)#end&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On the other side, configure &lt;code&gt;10.0.0.2&lt;/code&gt;. I am assuming Linux:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# ip addr add 10.0.0.2/32 peer 10.0.0.1 dev eth1&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;using-scp-to-pull-and-push-files&#34;&gt;Using SCP to Pull and Push Files&lt;/h3&gt;&lt;p&gt;Make sure SSH runs and you have a user configured with privilege level 15. Then,enable the SCP server on IOS:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1(config)#ip scp server enable&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can use &lt;a href=&#34;https://man.archlinux.org/man/core/openssh/scp.1.en&#34;&gt;&lt;code&gt;scp(1)&lt;/code&gt;&lt;/a&gt; to pull and push files. Notice, though, that inrecent times:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;scp uses the SFTP protocol over a ssh(1) connection for data transfer, anduses the same authentication and provides the same security as a loginsession.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;You can enable the legacy behavior with the &lt;code&gt;-O&lt;/code&gt; switch.&lt;/p&gt;&lt;p&gt;To pull a file called &lt;code&gt;A_FILENAME&lt;/code&gt; from IOS:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;linux$ scp -O stefan@10.1.1.1:flash:A_FILENAME ./target-filename&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you get the error &amp;ldquo;protocol error: filename does not match request&amp;rdquo;, youcould disable strict checking of filenames with the &lt;code&gt;-T&lt;/code&gt; flag.&lt;/p&gt;&lt;p&gt;To push a file called &lt;code&gt;A_FILENAME&lt;/code&gt; to IOS:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;linux$ scp -O ./A_FILENAME stefan@10.1.1.1:flash:A_FILENAME&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;pulling-a-file-over-http&#34;&gt;Pulling a File over HTTP&lt;/h3&gt;&lt;p&gt;Cisco IOS can reach out to an HTTP(S) server and pull a file from it. I like touse Busybox&amp;rsquo; &lt;code&gt;httpd&lt;/code&gt; for this:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;linux$ busybox httpd -vv -f -p 5000&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#copy http://10.0.0.2:5000/image.img flash:&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;10.0.0.1:13911: url:/file.txt10.0.0.1:13911: response:20010.0.0.1:26393: url:/file.txt10.0.0.1:26393: response:200&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice the duplicate requests coming from the router.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;h3 id=&#34;pushing-a-file-over-http&#34;&gt;Pushing a File over HTTP&lt;/h3&gt;&lt;p&gt;IOS can also run an HTTP(S) server and accept file uploads. For simplicity&amp;rsquo;ssake, I am only demonstrating how to configure file uploads over HTTP. Runningan HTTPS server is discussed in &lt;a href=&#34;https://relentlesscoding.com/posts/cisco-ios-tls-certs/&#34;&gt;another post&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;First, prepare the device to receive file over HTTP:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# mkdir flash:uploadsISR4321# conf tISR4321(config)# ip http serverISR4321(config)# ip http upload enable path flash:uploads&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -T isr4300.priv.pem \192.168.88.254/flash:uploads/isr4300.priv.pem&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://man.archlinux.org/man/core/curl/curl.1.en&#34;&gt;&lt;code&gt;curl(1)&lt;/code&gt;&lt;/a&gt;&amp;rsquo;s &lt;code&gt;-T, --upload-file &amp;lt;file&amp;gt;&lt;/code&gt; switch in combination with anHTTP URL will PUT the file on the remote destination. If you end the URL with aforward slash &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;curl(1)&lt;/code&gt; will append the local filename to the URL.&lt;/p&gt;&lt;p&gt;If you want to amend the filename on the Cisco file system:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# rename flash:uploads/incorrect.name flash:uploads/correct.name&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;tftp&#34;&gt;TFTP&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;linux# busybox udpsvd -vE 10.0.0.2 69 busybox tftpd -u ftp /srv/ftp&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Busybox&amp;rsquo; &lt;code&gt;tftpd&lt;/code&gt; is meant to be run by &lt;a href=&#34;https://en.wikipedia.org/wiki/Inetd&#34;&gt;&lt;code&gt;inetd&lt;/code&gt;&lt;/a&gt;. That daemon would bindto a port, and when a connection is made to that port, it spawns a process whichruns an executable. It hooks the network stream directly to stdin and stdout ofthe process. A big upside is that the process itself does not need to have anynetwork code in it.&lt;/p&gt;&lt;p&gt;Here, instead of starting &lt;code&gt;inetd&lt;/code&gt;, we take Busybox&amp;rsquo; suggestion and run the helperprogram called &lt;code&gt;udpsvd&lt;/code&gt; that works much like &lt;code&gt;inetd&lt;/code&gt; but runs as a foregroundprocess:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Create UDP socket, bind to IP:PORT and wait for incoming packets. Run PROGfor each packet, redirecting all further packets with same peer ip:port to it.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;We need to run as root to be able to bind to privileged port &lt;code&gt;69&lt;/code&gt;. &lt;code&gt;tftpd&lt;/code&gt; alsostarts as root to be able to &lt;code&gt;chroot&lt;/code&gt; into the specified directory, but thendrops its privileges to access the files as &lt;code&gt;-u &amp;lt;user&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;On the router we can now transfer files:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#copy tftp://10.0.0.2/file.txt flash:Destination filename [file.txt]?Accessing tftp://10.0.0.2/file.txt...Loading file.txt from 10.0.0.2 (via GigabitEthernet0/0/0): ![OK - 17 bytes]17 bytes copied in 0.018 secs (944 bytes/sec)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Also, depending on the TFTP software you are using, you might run into &lt;a href=&#34;https://www.cisco.com/c/en/us/support/docs/routers/10000-series-routers/23233-sw-upgrade-highendrouters-23233.html#:~:text=Most%20TFTP%20applications%20cannot%20transfer%20files%20larger%20than%2016MB&#34;&gt;thiscaveat&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Most TFTP applications cannot transfer files larger than 16MB in size. If theCisco IOS software you install is larger than 16MB, you must use an FTP or RCPserver.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I did not have this problem with &lt;code&gt;busybox tftpd&lt;/code&gt;, though.&lt;/p&gt;&lt;p&gt;A Cisco router can also function as a TFTP server, not only as a client. Toenable the TFTP server and serve a file called &lt;code&gt;A_FILENAME&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1(config)#tftp-server flash:A_FILENAME&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can now use an TFTP client to download the file:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;linux$ busybox tftp -g -r A_FILENAME 10.1.1.1&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;ftp&#34;&gt;FTP&lt;/h3&gt;&lt;p&gt;Transferring files over FTP works much the same as TFTP. Instead of using UDP,it uses TCP, though, so we have to change the Busybox&amp;rsquo; helper program:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;linux# busybox tcpsvd -vE 10.0.0.2 21 busybox ftpd -vv -a ftp /srv/ftp&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On the router:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#copy ftp://10.0.0.2/file.txt flash:Destination filename [file.txt]?Accessing ftp://10.0.0.2/file.txt...Loading file.txt[OK - 17/4096 bytes]17 bytes copied in 0.048 secs (354 bytes/sec)&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;over-a-serial-connection-with-minicom&#34;&gt;Over a Serial Connection With &lt;code&gt;minicom&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;(See also &lt;a href=&#34;https://relentlesscoding.com/posts/set-up-minicom-for-serial-access/&#34;&gt;another post on how to set up &lt;code&gt;minicom&lt;/code&gt;&lt;/a&gt;.)&lt;/p&gt;&lt;p&gt;On the internet, I read that in ROMMON, you can use something called &amp;ldquo;xmodem&amp;rdquo; totransfer files. &lt;code&gt;minicom&lt;/code&gt; has a &amp;ldquo;send files&amp;rdquo; feature where you can choose thexmodem protocol.&lt;/p&gt;&lt;p&gt;It should work like:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;rommon 123 &amp;gt;copy xmodem: flash:image.img&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, after entering ROMMON by booting up the router and pressing&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;A&lt;/kbd&gt; and then &lt;kbd&gt;F&lt;/kbd&gt; to send a BREAK, I see onlythe following options:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;rommon 1 &amp;gt; helpalias               set and display aliases commandboot                boot up an external processconfreg             configuration register utilitydev                 list the device tabledir                 list files in file systemhelp                monitor builtin command helphistory             monitor command historymeminfo             main memory informationrepeat              repeat a monitor commandreset               system resetset                 display the monitor variablesshowmon             display currently selected ROM monitorsync                write monitor environment to NVRAMtoken               display board&amp;#39;s unique token identifierunalias             unset an aliasunset               unset a monitor variable&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note there is no &lt;code&gt;copy&lt;/code&gt; command. (Nor an &lt;code&gt;tftpdlnd&lt;/code&gt; command I saw mentionedelsewhere. That command should, after setting the appropriate environmentvariables, download files over TFTP.)&lt;/p&gt;&lt;p&gt;I am not sure at this point if this feature is supposed to be supported by everyCisco router or not. Maybe I can unlock a privileged mode (akin to &lt;code&gt;enable&lt;/code&gt; inIOS) or update the ROMMON firmware and get extended options? It would suck forsure if all you had was ROMMON mode and no way to boot an image or get a workingimage in persistent memory.&lt;/p&gt;&lt;h2 id=&#34;speed-considerations&#34;&gt;Speed Considerations&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;TFTP is slow, because it uses UDP segments with a payload of 512 bytes each.&lt;a href=&#34;https://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol&#34;&gt;It is a &amp;ldquo;lockstep&amp;rdquo; protocol&lt;/a&gt;: each data packet needs to be acknowledged bythe receiver before the sender sends the next packet.&lt;/li&gt;&lt;li&gt;FTP and HTTP use TCP. TCP allows for more payload per segment and manysegments can be outstanding before the sender stops to wait for anacknowledgement. The receiver can acknowledge many segments at once, reducingoverhead. The receive window size (amount of bytes that can be outstanding atany one time before the sender has to wait for acknowledgement) can grow tomany megabytes.&lt;/li&gt;&lt;li&gt;Sneakernet should be even faster. If the router supports USB 2.0, you shouldbe able to expect around 30+ MiB/s depending on factors such as how fast therouter can flush the data to persistent storage.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;For example, the transfer of a 700+ MiB image was so tediously long over TFTP, Ihad to abort. FTP, on the other hand, completed in less than 5 minutes:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;776523079 bytes copied in 268.943 secs (2887315 bytes/sec)&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;security-considerations&#34;&gt;Security Considerations&lt;/h2&gt;&lt;p&gt;Protocols like HTTP, FTP and TFTP transmit data in plaintext and do not provideany protections against snooping and tampering. While we might not care muchabout confidentiality when transmitting a router image, we definitely want tomake sure we connect to the correct download server and get the correct imageloaded in our router. Accidental alterations during transmission, such as bitflips, are caught by transport layer (UDP/TCP) checksums: the faulty UDP/TCPsegment is discarded and the download server resends it automatically. Maliciousalterations by a MITM can be detected by calculating the digest of thereassembled file on the destination:&lt;/p&gt;&lt;p&gt;On your Linux download server:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;linux$ sha512sum isr4300-universalk9.17.09.04.SPA.bin34f8eff288bb485c8ca902d2e5c03f9d45a4dc881e919a6ae3f0ca3a52161eecc5c9fc7a7ed90423984ee690695293ad011590fa0e75c65ccf003f7f89b0d5c0  isr4300-universalk9.17.09.04.SPA.bin&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Compare that output with what you get on the router:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#verify /sha512 flash:isr4300-universalk9.17.09.04.SPA.bin 34f8eff288bb485c8ca902d2e5c03f9d45a4dc881e919a6ae3f0ca3a52161eecc5c9fc7a7ed90423984ee690695293ad011590fa0e75c65ccf003f7f89b0d5c0........................................................ &amp;lt;snip&amp;gt; ...Done!Verified (bootflash:isr4300-universalk9.17.09.04.SPA.bin) = 34f8eff288bb485c8ca902d2e5c03f9d45a4dc881e919a6ae3f0ca3a52161eecc5c9fc7a7ed90423984ee690695293ad011590fa0e75c65ccf003f7f89b0d5c0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To trust this digest, you need a secure connection to the router, however. Thiscan be a connection via the serial console or over the LAN, for example, or overSSH. If you do not have a secure connection to the destination, you cannot besure and maybe should not be servicing your router this way. Spend time to setup SSH in this case.&lt;/p&gt;&lt;p&gt;Also, &lt;a href=&#34;#making-sure-you-did-not-just-download-malware&#34;&gt;as mentioned at the top&lt;/a&gt;,&lt;a href=&#34;https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/sys-image-mgmt/configuration/xe-16-8/sysimgmgmt-xe-16-8-book/sysimgmgmt-dgtly-sgnd-sw.html&#34;&gt;if the image has something like &lt;code&gt;SPA&lt;/code&gt; in it, it should be digitally signed&lt;/a&gt;.I have not spent much time looking into this, but if you can be certain you havethe correct public key from Cisco, this would provide the strongest guaranteethat the image has not been tampered with.&lt;/p&gt;&lt;h2 id=&#34;specifying-which-image-to-run-on-startup&#34;&gt;Specifying Which Image to Run on Startup&lt;/h2&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;R1#conf tR1(config)#boot system flash isr4300-universalk9.17.12.04a.SPA.binR1(config)#endR1#copy running-config startup-configR1#reload&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Make sure to persist changes by writing the config to flash memory.&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai spell: --&gt;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&lt;hr&gt;&lt;ol&gt;&lt;li id=&#34;fn:1&#34;&gt;&lt;p&gt;That is why I could not get Busybox&amp;rsquo; &lt;code&gt;nc&lt;/code&gt; working:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;linux$ cat -A res.http#!/bin/shcat &amp;lt;&amp;lt;EOFHTTP/1.1 200 OK^M$Content-Type: text/plain^M$Content-Length: 14^M$^M$Hello, world!$EOF&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;linux$ chmod u+x res.http&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;linux$ busybox nc -lk -p 5000 -e ./res.httpconnect to 10.0.0.2:5000 from 10.0.0.1:16289 (10.0.0.1:16289)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wireshark shows &lt;code&gt;nc&lt;/code&gt; sending TCP RSTs after the first HTTP exchange with therouter, whereas maybe the router only tolerates graceful connection termination,over which we have no control when using &lt;code&gt;nc&lt;/code&gt;. (Same goes for using &lt;code&gt;busybox tcpsvd&lt;/code&gt; which I will discuss later.)&lt;/p&gt;&lt;p&gt;Then again, maybe these duplicate HTTP requests are just an idiosyncrasy of&lt;code&gt;isr4300-universalk9.16.06.04.SPA.bin&lt;/code&gt;, the version I am trying to upgrade from.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>VLANs</title>
       <link>https://relentlesscoding.com/posts/vlans/</link>
       <pubDate>Tue, 11 Feb 2025 11:27:00 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/vlans/</guid>
       <description>&lt;p&gt;VLANs have stumped me for the longest time. They are a requirement for networksegmentation, but couldn&amp;rsquo;t you also achieve that by subnetting? What &lt;em&gt;is&lt;/em&gt; therelationship between a VLAN and a subnet? Are they the same thing? In this post,I attempt to answer these and some other questions.&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;the-lan&#34;&gt;The LAN&lt;/h2&gt;&lt;p&gt;A LAN is a layer-2 broadcast segment. That means that layer-2 devices, such asswitches, will flood broadcasts, unknown multicasts and multicasts (&lt;a href=&#34;https://en.wikipedia.org/wiki/Broadcast%2C_unknown-unicast_and_multicast_traffic&#34;&gt;BUM&lt;/a&gt;) outof every interface except the one they received it on. Every NIC on every deviceon that same segment will have to spend CPU cycles examining the frame to see ifit is addressed to that NIC.&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/vlans/single-broadcast-domain.webp&#34; alt=&#34;A single broadcast domain: 4 end devices connected to a VLAN-agnosticswitch&#34;&gt;&lt;/p&gt;&lt;p&gt;In the diagram, if host A sends a BUM to its LAN (the orange arrow), the switchwill forward it out all of its other ports.&lt;/p&gt;&lt;p&gt;To create a second, separate broadcast domain, we need to buy a second switch(and not connect the switches together):&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/vlans/2-broadcast-domains-2-unconnected-switches.webp&#34; alt=&#34;2 broadcast domains implemented by 2 unconnectedswitches&#34;&gt;&lt;/p&gt;&lt;p&gt;Now, when host A sends traffic onto the link facing the switch, the BUM andunicast traffic will be flooded onto the segment connected with host B.Unsurprisingly, since the two switches are not connected, no traffic coming fromhost A or B will ever reach host C and D.&lt;/p&gt;&lt;p&gt;We have achieved traffic isolation, but at the price of being potentially verywasteful: if you have a 24-port switch and require 12 devices on 2 different LANsegments, you have to buy 2 switches, even though the single switch has enoughports to spare.&lt;/p&gt;&lt;h2 id=&#34;why-segment-traffic&#34;&gt;Why Segment Traffic&lt;/h2&gt;&lt;p&gt;But let&amp;rsquo;s back up a little and think about why we might want to segment trafficat all:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;We might want to limit CPU cycles spent by NICs on all BUM traffic.&lt;/li&gt;&lt;li&gt;We might not want snoopers to have access to all BUM traffic.&lt;/li&gt;&lt;li&gt;We might want to apply different security policies to different kinds ofdevices and/or traffic (think network management traffic such as SSH bytrusted IT personel versus guest Wi-Fi).&lt;/li&gt;&lt;li&gt;We might want people in different physical locations be part of the samebroadcast domain.&lt;/li&gt;&lt;li&gt;We might want to keep the STP tree simple and manageable.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;side-note-why-not-just-route-everything&#34;&gt;Side Note: Why Not Just Route Everything?&lt;/h2&gt;&lt;p&gt;When I was building my first networks, I thought, why not just use routing? Thatis, why not just put every device on its own IP subnet and route all trafficbetween devices?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;While that definitely works, because routing also gives us traffic isolation,it carries more overhead: a router needs to examine the IP header and changeit (TTL, recalculate checksum), whereas a switch looks up the destinationaddress in its CAM table and immediately forwards the frame. Switching, in aword, is faster (wire speed) than routing (10-50+ μs per hop).&lt;/li&gt;&lt;li&gt;Also, switching requires less or no configuration: you plug the switch in,power it up, attach devices and they can talk, all without setting up(additional) IP subnets.&lt;/li&gt;&lt;li&gt;Also, routers (and L3 switches) are more expensive and generally have lessports.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;introducing-vlans&#34;&gt;Introducing VLANs&lt;/h2&gt;&lt;p&gt;VLANs are virtual broadcast domains. They are identified by a 12-bit number(0-4095, although 0 and 4094 and some others are reserved) that groups BUMtraffic together. Traffic flowing in different VLANs is completely isolated, asif we had 2 separate, unconnected switches:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/vlans/2-vlans-1-switch.webp&#34; alt=&#34;Logical view of 2 VLANs implemented on a single switch creating 2 broadcastdomains&#34;&gt;&lt;/p&gt;&lt;p&gt;In the diagram, traffic from host A can only ever reach host B or be discarded.VLANs guarantee that no traffic will ever flow from host A or B to host C or D,just as if the switches were physically separated.&lt;/p&gt;&lt;p&gt;To make all traffic coming in on a link part of a VLAN, we can make that portpart of the VLAN by assigning it a VLAN ID.&lt;/p&gt;&lt;p&gt;To pass VLAN traffic from switch to switch, we need to somehow tell the otherswitch which VLAN the traffic belongs to. The easiest approach is to use a PortVLAN ID (PVID or access port) on both sides and use a link per VLAN:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/vlans/multiswitch-vlan-without-trunking.webp&#34; alt=&#34;Multiswitch VLAN withouttrunking&#34;&gt;&lt;/p&gt;&lt;p&gt;Obviously, that doesn&amp;rsquo;t scale very well. You would need a separate link forevery VLAN. Image having not 2, but 10 or 30 VLANs and soon most of the ports ofour switches are unavailable for end devices (the reason we have these switchesin the first place).&lt;/p&gt;&lt;p&gt;Enter &lt;strong&gt;trunks&lt;/strong&gt;: we use a single link between switches but we embed the VLAN IDinside the frame:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/vlans/multiswitch-vlan-with-trunking.webp&#34; alt=&#34;Multiswitch VLAN with trunking&#34;&gt;&lt;/p&gt;&lt;p&gt;This approach has been standardized in &lt;a href=&#34;https://en.wikipedia.org/wiki/IEEE_802.1Q&#34;&gt;the IEEE 802.1Q standard&lt;/a&gt;:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/vlan/ethernet-802dot1q.webp&#34; alt=&#34;Ethernet frame with IEEE 802.1Q tag&#34;&gt;&lt;/p&gt;&lt;p&gt;The 802.1Q field is inserted before the EtherType field and has type &lt;code&gt;0x8100&lt;/code&gt;itself. The receiving switch strips the additional field from the Ethernetheader and forwards it towards its destination.&lt;/p&gt;&lt;h2 id=&#34;routing-traffic-between-vlans&#34;&gt;Routing Traffic between VLANs&lt;/h2&gt;&lt;p&gt;Traffic being completely isolated from each other is great, except when you needto talk to a device in another VLAN. To do so, a layer-3 device, such as arouter or a layer-3 switch, is needed. Every VLAN typically gets assigned itsown subnet, and routers route between these subnets and thus between the VLANs.&lt;/p&gt;&lt;p&gt;One of the most typical topologies for routing between VLANs is the so-calledRouter-on-a-Stick (ROAS):&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/vlans/roas.webp&#34; alt=&#34;A router-on-a-stick with a switch connected over a trunk link to arouter&#34;&gt;&lt;/p&gt;&lt;p&gt;Here, a switch that knows about several VLANs is connected with a single trunkport to a router. The router uses virtual subinterfaces that each know about aseparate VLAN. Those subinterfaces are assigned IP addresses in the VLAN subnet.Upon receiving traffic that is part of a specific VLAN, the router decapsulatesthe Ethernet frame, inspects the IP header, selects the outgoing interface,encapsulates the packet into a new Ethernet frame including an 802.1Q field, andsends it out of the virtual subinterface (over the single physical trunk port)back to the switch.&lt;/p&gt;&lt;h2 id=&#34;configure-cisco&#34;&gt;Configure Cisco&lt;/h2&gt;&lt;p&gt;On a Cisco switch, configure an access port as follows:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Switch(config)#int gi 0/1Switch(config-if)#switchport mode accessSwitch(config-if)#switchport access vlan 10&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Verify:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Switch#show vlan&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To create a trunk port:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Switch(config-if)#switchport encapsulation dot1qSwitch(config-if)#switchport mode trunk&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To allow which tags are allowed on the trunk:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Switch(config-if)#switchport trunk allowed vlan 10,20&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Verify:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Switch#show interface trunk&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;configure-mikrotik&#34;&gt;Configure MikroTik&lt;/h2&gt;&lt;h3 id=&#34;throughput-disclaimer&#34;&gt;Throughput Disclaimer&lt;/h3&gt;&lt;p&gt;On MikroTik devices, things are vastly more configurable, more complex andtherefore more confusing. First, there is no single configuration that works onall devices. Some switches have specialized hardware (docs &lt;a href=&#34;https://help.mikrotik.com/docs/spaces/ROS/pages/103841826/Basic+VLAN+switching&#34;&gt;here&lt;/a&gt; and&lt;a href=&#34;https://help.mikrotik.com/docs/spaces/ROS/pages/328068/Bridging+and+Switching&#34;&gt;here&lt;/a&gt;) and the configuration reflects that. You can increase throughput bymaking use of hardware processing instead of using the CPU. While I&amp;rsquo;ve found theconfig below to work on a couple of MikroTik devices including a CRS328 L3switch, I cannot guarantee that it is the best way to do it on all hardware.Read the documentation.&lt;/p&gt;&lt;h3 id=&#34;bridges-bridge-ports-vlan-interfaces-and-tagged-and-untagged-egress&#34;&gt;Bridges, Bridge Ports, VLAN interfaces and Tagged and Untagged Egress&lt;/h3&gt;&lt;p&gt;With that out of the way, here, I am going to configure a &lt;a href=&#34;https://mikrotik.com/product/RB750Gr3&#34;&gt;hEX router&lt;/a&gt; thathas 5 ports, &lt;code&gt;ether1&lt;/code&gt; through &lt;code&gt;ether5&lt;/code&gt;. &lt;code&gt;ether3&lt;/code&gt; and &lt;code&gt;ether4&lt;/code&gt; are going to beaccess VLANs, &lt;code&gt;ether5&lt;/code&gt; is going to be a trunk port.&lt;/p&gt;&lt;p&gt;First you create a bridge:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[u@hEX] /interface/bridge/add name=BR[u@hEX] /interface/bridge/print proplist=name,pvid,vlan-filteringFlags: D - dynamic; X - disabled, R - running  0  R name=&amp;#34;BR1&amp;#34; pvid=1 vlan-filtering=no&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice that &lt;code&gt;vlan-filtering&lt;/code&gt; says &lt;code&gt;no&lt;/code&gt; for the time being to ease configuration.&lt;/p&gt;&lt;p&gt;Add VLAN interfaces:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[u@hEX] /interface/vlan/add name=GUEST_WIFI vlan-id=10 interface=BR[u@hEX] /interface/vlan/add name=MGMT vlan-id=20 interface=BR&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Optionally add IP addresses to those VLAN interfaces so that the MikroTik can bereached on those IPs.&lt;/p&gt;&lt;p&gt;Add ports to the bridge and make &lt;code&gt;ether3&lt;/code&gt; and &lt;code&gt;ether4&lt;/code&gt; access ports by settingtheir PVID:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[u@hEX] /interface/bridge/port/add bridge=BR interface=ether3 pvid=10[u@hEX] /interface/bridge/port/add bridge=BR interface=ether4 pvid=20[u@hEX] /interface/bridge/port/add bridge=BR interface=ether5&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now for &lt;a href=&#34;https://help.mikrotik.com/docs/spaces/ROS/pages/28606465/Bridge+VLAN+Table&#34;&gt;the interesting part&lt;/a&gt;: we need to determine at which point duringits journey from the physical interface, over the bridge to the VLAN interfacean incoming packet should be tagged&lt;/p&gt;&lt;p&gt;I am not completely sure, but it seems to me that a packet should always have atag on the bridge. How else could the bridge determine to what VLAN interface tosend it? Access ports should send traffic untagged on egress, trunk ports shouldsend traffic tagged. And so we end up with the following configuration foraccess ports 3 and 4:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[u@hEX] /interface/bridge/vlan/add bridge=BR vlan=10 \\... tagged=BR untagged=ether3[u@hEX] /interface/bridge/vlan/add bridge=BR vlan=20 \\... tagged=BR untagged=ether4&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Traffic on the trunk port should be tagged both on the bridge and the egressinterface:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[u@hEX] /interface/bridge/vlan/add bridge=BR vlan=10,20 \\... tagged=BR,ether5&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, don&amp;rsquo;t forget to enable PVID behavior and VLAN filtering:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[u@hEX] /interface/bridge/set [find name=BR] vlan-filtering=yes&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;configure-linux&#34;&gt;Configure Linux&lt;/h2&gt;&lt;p&gt;Linux is the underlying OS for both Cisco&amp;rsquo;s &lt;a href=&#34;https://en.wikipedia.org/wiki/Cisco_IOS_XE&#34;&gt;IOS XE&lt;/a&gt; and MikroTik&amp;rsquo;s RouterOSso it should surprise no-one that you can configure everything in this articleon Linux as well.&lt;/p&gt;&lt;p&gt;However, the one thing I found to be useful, is tagging my Linux traffic with802.1Q tags. To do that, create a subinterface and configure the tag:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;linux# ip link add link eth0 name eth0.10 type vlan id 10linux# ip address add 10.10.10.2/24 dev eth0.10linux# ip link set eth0.10 up&lt;/code&gt;&lt;/pre&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai spell: --&gt;</description>
     </item>
   
     <item>
       <title>How to Enable a DHCP Client on a Cisco Interface</title>
       <link>https://relentlesscoding.com/posts/cisco-ios-dhcp-client/</link>
       <pubDate>Tue, 11 Feb 2025 10:55:07 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/cisco-ios-dhcp-client/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s look at how to automatically get an IP addresses assigned on an interfaceon a Cisco device. This could not be simpler.&lt;/p&gt;&lt;!-- more --&gt;&lt;p&gt;To make interface &lt;code&gt;g0/0/0&lt;/code&gt; a DHCP client:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# int g0/0/0ISR4321(config-if)# ip address dhcpISR4321(config-if)# no shutdown&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If there is a DHCP server on the network, the interfaces DHCP Discover probeswill be honored and the interface will now have an IP address:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# sh ip int briefInterface             IP-Address      OK?  Method  Status  ProtocolGigabitEthernet0/0/0  192.168.88.254  YES  DHCP    up      upGigabitEthernet0/0/1  unassigned      YES  unset   down    downGigabitEthernet0      unassigned      YES  unset   down    down&lt;/code&gt;&lt;/pre&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;</description>
     </item>
   
     <item>
       <title>Authentication on Cisco IOS</title>
       <link>https://relentlesscoding.com/posts/cisco-ios-authentication/</link>
       <pubDate>Tue, 11 Feb 2025 08:35:09 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/cisco-ios-authentication/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s have a look at how Cisco IOS handles authentication and how passwords arestored in the configuration file.&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;disable-cleartext-passwords-in-the-configuration-file&#34;&gt;Disable Cleartext Passwords in the Configuration File&lt;/h2&gt;&lt;p&gt;Let&amp;rsquo;s I set the &amp;ldquo;front-door password&amp;rdquo; for console access that would put the userin user EXEC mode:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# conf tISR4321(config)# line console 0ISR4321(config-line)# password foobarbazISR4321(config-line)# loginISR4321(config-line)# ^Z&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# sh run | b lineline con 0!line aux 0!line vty 0 4 password foobarbaz loginline vty 5 15 password foobarbaz login!&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To obfuscate passwords in the configuration file:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# service password-encryption&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Passwords that are normally be stored in plaintext in the config file will nowbe obscured by &amp;ldquo;Type 7 encryption&amp;rdquo;.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; &amp;ldquo;Encryption&amp;rdquo; sounds like a big deal, butthis type of encryption is merely meant to prevent casual shoulder surfing:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;line con 0 password 7 1047021200 login&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.infosecmatter.com/cisco-password-cracking-and-decrypting-guide/#decrypt-cisco-type-7-password&#34;&gt;Type 7 &amp;ldquo;encryption&amp;rdquo; is seriously broken&lt;/a&gt;. So consider anyone with access tothe config (or backups thereof) to know the password and be able to access userEXEC mode on your Cisco devices.&lt;/p&gt;&lt;p&gt;If we were to &lt;code&gt;exit&lt;/code&gt; now, we would be prompted for this front-door password:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# exitISR4321 con0 is now availablePress RETURN to get started.User Access VerificationPassword: ISR4321&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The router does not echo the password back to the screen to prevent shouldersurfing.&lt;/p&gt;&lt;h2 id=&#34;protect-privileged-exec-mode-with-a-password&#34;&gt;Protect Privileged EXEC Mode with a Password&lt;/h2&gt;&lt;p&gt;Set the &amp;ldquo;enable&amp;rdquo; password (to get to enable/privileged EXEC mode):&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# enable secret algorithm-type scrypt secret &amp;lt;pwd&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At least on my IOS, fortunately, &lt;a href=&#34;https://en.wikipedia.org/wiki/Scrypt&#34;&gt;&lt;code&gt;scrypt&lt;/code&gt;&lt;/a&gt; is available as a PBKDF.&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;When &lt;code&gt;enable secret&lt;/code&gt; is set, IOS will ignore the &lt;code&gt;enable password&lt;/code&gt;. That meansthat if both are set, you can only gain access with the &lt;code&gt;enable secret&lt;/code&gt;password. &lt;code&gt;enable password&lt;/code&gt; stores the password in plaintext in the config file,or merely obfuscates it when &lt;code&gt;service password-encryption&lt;/code&gt; is set. So removethis insecure password with &lt;code&gt;no enable password&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;username--password-authentication&#34;&gt;Username + Password Authentication&lt;/h2&gt;&lt;p&gt;You can also use username + password authentication. Logins over console andTelnet can then request a username in addition to a password. SSH alwaysrequires both a username and a password.&lt;/p&gt;&lt;p&gt;Create users with usernames, password and privilege levels:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# username stefan privilige 15 secret stefanpwdISR4321(config)# username thomas privilige 1 secret stefanpwd&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can use &lt;code&gt;algorithm-type&lt;/code&gt; to specify the PBKDF. On my IOS, by default, ituses the (weak) MD5. Use &lt;code&gt;scrypt&lt;/code&gt; instead.&lt;/p&gt;&lt;p&gt;By changing &lt;code&gt;line console 0&lt;/code&gt; to use &lt;code&gt;login local&lt;/code&gt; instead of &lt;code&gt;login&lt;/code&gt;, the loginprompt will request credentials from the local list configured with &lt;code&gt;username &amp;lt;name&amp;gt; secret &amp;lt;secret&amp;gt;&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# conf tISR4321(config)# line console 0ISR4321(config-line)# login localISR4321(config-line)# endISR4321# exitUser Access VerificationUsername: thomasPassword:ISR4321&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we authenticate with the privileged user &lt;code&gt;stefan&lt;/code&gt; instead (which wasconfigured with &lt;code&gt;privilege 15&lt;/code&gt;), we are taken straight to enable/privileged EXECmode after authentication.&lt;/p&gt;&lt;h2 id=&#34;secure-ssh-andor-telnet-with-acls&#34;&gt;Secure SSH and/or Telnet with ACLs&lt;/h2&gt;&lt;p&gt;To secure logging in over IP, ACLs are needed. After all, you can have a strongpassword, but if Cisco&amp;rsquo;s SSH or Telnet daemon has a vulnerability, you wouldstill want to prevent the baddies from knocking on those exposed ports.&lt;/p&gt;&lt;p&gt;First, create an standard numbered ACL:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)#access-list 1 permit 172.16.0.0 0.15.255.255&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This permits all hosts with a source IP address in the RFC 1918 private IPsubnet 172.16/12. ACLs in Cisco land always end with default deny. So thisreads: allow IP packets from source 172.16/12 and drop the packets otherwise.The wildcard mask (&lt;code&gt;0.15.255.255&lt;/code&gt;) is a mask that defines which bits of thesource address will be checked against the defined ACL base address.&lt;/p&gt;&lt;p&gt;Say, a packet with source IP 172.16.123.123 comes in:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;base  = 1010 1100 0001 0000 0000 0000 0000 0000mask  = 0000 0000 0000 1111 1111 1111 1111 1111check = 1010 1100 0001 0000 0111 1011 0111 1011&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we compare &lt;code&gt;base&lt;/code&gt; with &lt;code&gt;check&lt;/code&gt; for every bit where the &lt;code&gt;mask&lt;/code&gt; has a &lt;code&gt;0&lt;/code&gt; bit,we see that &lt;code&gt;base == check&lt;/code&gt;. And so the ACL matches and traffic is allowed toflow. Internally, Cisco probably just logically ORs the &lt;code&gt;base&lt;/code&gt; and &lt;code&gt;mask&lt;/code&gt; (&lt;code&gt;base | mask&lt;/code&gt;) and &lt;code&gt;mask&lt;/code&gt; and &lt;code&gt;check&lt;/code&gt; (&lt;code&gt;mask | check&lt;/code&gt;) and compares the results.&lt;/p&gt;&lt;p&gt;Currently, this ACL is not assigned to a port or a vty though, so it does not doanything.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)#line vty 0 15ISR4321(config-line)#access-class 1 in&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here, we assign the standard numbered ACL with identifier &lt;code&gt;1&lt;/code&gt; to all the vtylines.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;in&lt;/code&gt; keyword specifies the direction of the traffic flow: in this case, weanticipate a remote client initiating a connection with the Cisco device. Youcould also specify &lt;code&gt;out&lt;/code&gt;. The ACL would then filter outgoing connection madefrom the Cisco device by the user connected over a vty line. The single IPaddress in the standard ACLs is then interpreted as a &lt;em&gt;destination&lt;/em&gt; rather thana &lt;em&gt;source&lt;/em&gt; IP address. When using extended ACLs (100-199 and 2000-2699) for&lt;code&gt;access-class&lt;/code&gt;, you should use the &lt;code&gt;any&lt;/code&gt; keyword for either the source ordestination address, depending on whether ingress or egress is filtered.&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&lt;hr&gt;&lt;ol&gt;&lt;li id=&#34;fn:1&#34;&gt;&lt;p&gt;Note that if you disable &lt;code&gt;password-encryption&lt;/code&gt; again, IOS will not decryptthe obfuscated passwords.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:2&#34;&gt;&lt;p&gt;By default, my IOS version (16.06.04) uses the (weak) MD5-based BSDpassword algorithm 1. If you encounter an enable secret:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Router#sh run | i secretenable secret 5 $1$mERr$jegsBINbJe1/UUQKc.NUC1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can check the password on a Linux box with &lt;a href=&#34;https://man.archlinux.org/man/core/openssl/openssl-passwd.1ssl.en&#34;&gt;&lt;code&gt;openssl-passwd(1)&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;openssl passwd -1 -salt mERr foobarbaz$1$mERr$jegsBINbJe1/UUQKc.NUC1&lt;/code&gt;&lt;/pre&gt;&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Set the Time Manually or with NTP on Cisco IOS</title>
       <link>https://relentlesscoding.com/posts/cisco-ios-set-time-manually-ntp/</link>
       <pubDate>Tue, 11 Feb 2025 08:20:46 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/cisco-ios-set-time-manually-ntp/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s take a look at how to set the clock on a Cisco device.&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;display-the-current-time&#34;&gt;Display the Current Time&lt;/h2&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321&amp;gt; show clock13:11:59.001 UTC Sun Mar 2 2025&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;manually-set-the-time&#34;&gt;Manually Set the Time&lt;/h2&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# clock set 13:08:10 2 March 2025&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;configure-ntp&#34;&gt;Configure NTP&lt;/h2&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# ntp server 192.168.88.1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Set timezone and DST:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# conf tISR4321(config)# clock timezone CET +1ISR4321(config)# clock summer-time CEST recurring last Sunday March 02:00 last Sunday October 03:00ISR4321(config)# end&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After &lt;code&gt;recurring&lt;/code&gt;, you can indicate when DST time starts. In this case, it readssomething like &amp;ldquo;summer time starts the last Sunday of March at 02:00 in themorning; winter time starts the last Sunday of October at 03:00 in the morning.&amp;rdquo;&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;</description>
     </item>
   
     <item>
       <title>Make Cisco Device Start Up From NVRAM Again</title>
       <link>https://relentlesscoding.com/posts/make-cisco-ios-start-up-from-nvram-again/</link>
       <pubDate>Tue, 11 Feb 2025 08:05:13 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/make-cisco-ios-start-up-from-nvram-again/</guid>
       <description>&lt;p&gt;My ISR4321 was not remembering its config on startup even though I was sure to&lt;code&gt;write memory&lt;/code&gt; after making config changes. Turns out, the configurationregistry was not set correctly. This post looks at what this registry containsand how to make sure it will load the stored config on bootup.&lt;/p&gt;&lt;!-- more --&gt;&lt;p&gt;The configuration registry is 2 bytes represented as 4 hex digits. In my case,the it was set to &lt;code&gt;0x9922&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://www.cisco.com/c/en/us/support/docs/routers/10000-series-routers/50421-config-register-use.html&#34;&gt;Here is an overview of what all the different bits mean&lt;/a&gt;. When breaking itdown:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;0x8000 =&amp;gt; enables diagnostic messages, ignores NVRAM contents0x1000 =&amp;gt; console line speed0x0800 =&amp;gt; console line speed0x0100 =&amp;gt; break disabled0x0020 =&amp;gt; console line speed0x0002 =&amp;gt; boots into ROM if inital boot fails&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These values are ANDed. The first value, 0x8000, causes IOS to ignore thestartup-config from NVRAM. We change that value:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# conf tISR4321(config)# config-register 0x3922&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The default is &lt;code&gt;0x2102&lt;/code&gt;, but I want the fasted baud rate (115200) so I add&lt;code&gt;0x1820&lt;/code&gt; to get &lt;code&gt;0x3922&lt;/code&gt;.&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;</description>
     </item>
   
     <item>
       <title>Create or Import TLS Certificates on Cisco IOS</title>
       <link>https://relentlesscoding.com/posts/cisco-ios-tls-certs/</link>
       <pubDate>Tue, 11 Feb 2025 07:28:44 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/cisco-ios-tls-certs/</guid>
       <description>&lt;p&gt;You can generate a self-signed certificate on Cisco IOS. &lt;a href=&#34;https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/sec_conn_pki/configuration/15-mt/sec-pki-15-mt-book/sec-cert-enroll-pki.html&#34;&gt;If that fails&lt;/a&gt;, youcan generate one on another machine and import it.&lt;/p&gt;&lt;!-- more --&gt;&lt;h3 id=&#34;let-ios-generate-a-self-signed-certificate&#34;&gt;Let IOS Generate a Self-Signed Certificate&lt;/h3&gt;&lt;p&gt;First, we create a CA:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# conf tISR4321(config)# crypto pki trustpoint mytpISR4321(ca-trustpoint)# enrollment selfsignedISR4321(ca-trustpoint)# fqdn isr4300.testISR4321(ca-trustpoint)# ip-address 192.168.88.254ISR4321(ca-trustpoint)# subject-alt-name isr4300.testISR4321(ca-trustpoint)# hash sha256&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, we can create a self-signed TLS certificate:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# crypto pki enroll mytpMar  4 15:28:56.870: %CRYPTO-6-AUTOGEN: Generated new 1024 bit key pair% Include the router serial number in the subject name? [yes/no]: noGenerate Self Signed ISR4321 Certificate? [yes/no]: yesISR4321 Self Signed Certificate successfully created&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Inspect the just-created certificate details:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# sh crypto pki certificates mytpISR4321 Self-Signed Certificate  Status: Available  Certificate Serial Number (hex): 02  Certificate Usage: General Purpose  Issuer:    serialNumber=FDO2431M1QP+hostname=ISR4300+ipaddress=192.168.88.254  Subject:    Name: ISR4300    IP Address: 192.168.88.254    Serial Number: FDO2431M1QP    serialNumber=FDO2431M1QP+hostname=ISR4300+ipaddress=192.168.88.254  Validity Date:    start date: 16:29:19 CET Mar 4 2019    end   date: 01:00:00 CET Jan 1 2020  Associated Trustpoints: mytp&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Export the certificate:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# conf tISR4321(config)# crypto pki export mytp pem terminal% Self-signed CA certificate:-----BEGIN CERTIFICATE-----MIICWTCCAcKgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBIMUYwEgYDVQQFEwtGRE8yNDMxTTFRUDATBgkqhkiG9w0BCQIWBlJvdXRlcjAbBgkqhkiG9w0BCQgTDjE5Mi4xNjguODguMjU0MB4XDTE5MDMwNDE1MjkxOVoXDTIwMDEwMTAwMDAwMFowSDFGMBIGA1UEBRMLRkRPMjQzMU0xUVAwEwYJKoZIhvcNAQkCFgZSb3V0ZXIwGwYJKoZIhvcNAQkIEw4xOTIuMTY4Ljg4LjI1NDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtuJpgwpPUOxiYLxfMsL9kiQa5i0l2kmwYbyTm0XlqjEwOk8eRcyNQHybnyDBrmtuP4nlT/BkY3r0ZG7qD6fqg3xqZ5oOZe3w3vsvOF6U2G+eBbWAF8uu5BBT63r7rKcIg1mhEcAHCjEiGbDB4qQEKxEooELIlgjlEL3RAAdVkEECAwEAAaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBQcHfCtlFrqWC2SaBVzHS7XwxGF6DAdBgNVHQ4EFgQUHB3wrZRa6lgtkmgVcx0u18MRhegwDQYJKoZIhvcNAQEFBQADgYEAVP2ScAXMJllCPuvfyEyYVJW5H/iCgG1HV7CSI1IucGadRjJnd1hiIpoSXHRZ0wACkRXtsfJEnQmK52YHFUnlJr3nMlUluNZWroRMubAh3t04Rrr+OBIIc9q3BjTbzQtIE34u/o3hUya8mJKHf/+SxAhnbS/jOCkIT/PzwHwEmUw=-----END CERTIFICATE-----% RSA keypair &amp;#39;ISR4321&amp;#39; is not exportable.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Copy and past into &lt;code&gt;export.crt&lt;/code&gt; and inspect with &lt;code&gt;openssl x509 -in export.crt -noout -text&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&#34;import-a-tls-certificate&#34;&gt;Import a TLS Certificate&lt;/h3&gt;&lt;p&gt;You can also create a certificate on another machine and import into IOS.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Generate key pair:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048 \    -out key.pem  &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By default, OpenSSL version 3 when invoked with &lt;code&gt;openssl genpkey&lt;/code&gt; creates aPKCS#8 file (&lt;code&gt;BEGIN PRIVATE KEY&lt;/code&gt;). (My version of) IOS only seems to acceptencrypted PKCS#1 (&lt;code&gt;BEGIN RSA PRIVATE KEY&lt;/code&gt;).&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Convert to PKCS#1 (&lt;code&gt;-traditional&lt;/code&gt;) and encrypt with 3DES (&lt;code&gt;-des3&lt;/code&gt;):&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;openssl pkey -in key.pem -traditional -des3 -passout pass:foobarbaz    -out key.enc.pem&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Generate cert (valid for 10 years):&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;openssl req -x509 \    -key key.pem \    -out isr4300.crt \    -days 3700 \    -subj /CN=isr4300.test \    -addext &amp;#39;subjectAltName=DNS:isr4300.test,IP:192.168.88.254&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/sec_conn_pki/configuration/15-mt/sec-pki-15-mt-book/sec-cert-enroll-pki.html&#34;&gt;Import this as a &amp;ldquo;trustpoint&amp;rdquo; into Cisco IOS&lt;/a&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4300(config)# crypto pki import mytp pem terminal password foobarbaz&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We see the following in &lt;code&gt;running-config&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;crypto pki trustpoint mytp enrollment pkcs12 revocation-check crl rsakeypair mytp!!crypto pki certificate chain mytp certificate ca 7FBC7EFBC685713BD7225B9AF3E8C7A3E0486078   3082032E 30820216 A0030201 0202147F BC7EFBC6 85713BD7 225B9AF3 E8C7A3E0   ...   quit&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&#34;enable-tls-to-create-secure-server&#34;&gt;Enable TLS to Create Secure Server&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# ip http secure-trustpoint mytpISR4321(config)# ip http secure-server&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And disable cleartext HTTP:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# no ip http server&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You should now be able to access WebUI over TLS:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -k https://192.168.88.254/webui&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Caution: &lt;a href=&#34;https://man.archlinux.org/man/core/curl/curl.1.en&#34;&gt;&lt;code&gt;curl(1)&lt;/code&gt;&lt;/a&gt;&amp;rsquo;s &lt;code&gt;-k, --insecure&lt;/code&gt; flag disables ALL TLS checks andjust accepts whatever certificate is presented to it: does not check whether thehostname matches nor whether it was signed by a trusted CA. Which is what youwant if you are using a self-signed certificate or a leaf certificate signed bya CA that is not in your certificate store, but otherwise do not use this.&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&lt;hr&gt;&lt;ol&gt;&lt;li id=&#34;fn:1&#34;&gt;&lt;p&gt;You can see the difference in ASN.1 structure with &lt;a href=&#34;https://man.archlinux.org/man/core/openssl/openssl-asn1parse.1ssl.en&#34;&gt;&lt;code&gt;openssl asn1parse -i -in &amp;lt;file&amp;gt;&lt;/code&gt;&lt;/a&gt;. (The &lt;code&gt;-i&lt;/code&gt; flag &amp;ldquo;indents the output according to the &amp;lsquo;depth&amp;rsquo; ofthe structures&amp;rdquo;.)&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Cisco IOS SSH</title>
       <link>https://relentlesscoding.com/posts/cisco-ios-ssh/</link>
       <pubDate>Mon, 10 Feb 2025 16:43:41 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/cisco-ios-ssh/</guid>
       <description>&lt;p&gt;While connecting to a Cisco device out-of-band (i.e. not using IP) using aserial connection is the most secure way to configure the device, we also needto be able to connect over the network. Telnet works, but is not secure. So weneed SSH. This posts looks at how to set up SSH with username and password on aCisco device, how to use public-key certificates as a best practice toauthenticate and how to disable SSH altogether if it is not needed.&lt;/p&gt;&lt;!-- more --&gt;&lt;p&gt;&lt;a href=&#34;https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/sec_usr_ssh/configuration/15-e/sec-usr-ssh-15-e-book/sec-secure-shell-v2.html&#34;&gt;Cisco, of course, has pages upon pages of discussion on the most commonoperations with SSH with examples&lt;/a&gt;. They are the authoritative source, soconsult their documentation when necessary.&lt;/p&gt;&lt;h2 id=&#34;set-up-ssh-with-username--password&#34;&gt;Set Up SSH with Username &amp;amp; Password&lt;/h2&gt;&lt;p&gt;Before you can generate an RSA server host key, the hostname and the domain nameof the device need to be set. Together, those make a Fully-Qualified Domain Name(FQDN). That is how Cisco IOS identifies the key. Although it&amp;rsquo;s not entirelyclear to me why it needs this FQDN before generating an RSA key pair.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Router# conf tRouter(config)# hostname ISR4300ISR4321(config)# ip domain-name example.invalid&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, we can generate the host key:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# crypto key generate rsaThe name for the keys will be: ISR4300.example.invalidChoose the size of the key modulus in the range of 360 to 4096 for yourGeneral Purpose Keys. Choosing a key modulus greater than 512 may takea few minutes.How many bits in the modulus [512]: 3072% Generating 3072 bit RSA keys, keys will be non-exportable...[OK]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note: according to NIST, [3072 bit RSA keys are recommended and consideredsecure through 2030 and beyond][NIST]. The newest versions of IOS should alsosupport Ed25519.&lt;/p&gt;&lt;p&gt;SSH only works with usernames in addition to passwords&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# username stefan secret stefan&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Enable only SSH authentication (not telnet or anything else):&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# conf tISR4321(config)# line vty 0 15ISR4321(config-line)# transport input sshISR4321(config-line)# login localISR4321(config-line)# no passwordISR4321(config-line)# end&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you set &lt;code&gt;login&lt;/code&gt; with a &lt;code&gt;password&lt;/code&gt;, 1 of 2 things might happen:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Configured users can use their username but share the same password&lt;/li&gt;&lt;li&gt;Nobody can log in, neither with their own password, nor with the sharedpassword.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;So setting &lt;code&gt;login local&lt;/code&gt; gets rid of this confusing mess: it will enableeverybody to use their own credentials.&lt;/p&gt;&lt;p&gt;For good measure, enable only SSH 2, and harden the configuration a bit:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# ip ssh version 2ISR4321(config)# ip ssh authentication-retries 2ISR4321(config)# ip ssh time-out 60&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;ip ssh authentication-retries 2&lt;/code&gt; shuts down the connection after 2 failedauthentication attempts.&lt;/p&gt;&lt;p&gt;&lt;code&gt;ip ssh time-out 60&lt;/code&gt; shuts down the connection when not receiving authenticationfrom the client after a minute. (In my experimentation, it looks like IOS mightnot respect a timeout of less than 30 seconds, though.)&lt;/p&gt;&lt;p&gt;Show information about the configured SSH server:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# sh ip sshSSH Enabled - version 2.0Authentication timeout: 60 secs; Authentication retries: 2&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Show information about connected SSH clients:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;SW01#sh sshConnection  Version  Mode  Encryption  Hmac       State             Username  0           1.99     IN    aes128-cbc  hmac-sha1  Session Started   stefan0           1.99     OUT   aes128-cbc  hmac-sha1  Session Started   stefan&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Change how long an SSH or Telnet session lingers with &lt;code&gt;exec-timeout &amp;lt;min&amp;gt; &amp;lt;sec&amp;gt;&lt;/code&gt;. By default, the SSH server disconnects after 5 minutes. Change it to 30minutes and 50 seconds:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# line vty 0 15ISR4321(config-line)# exec-timeout 30 50&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;use-public-keys-instead-of-passwords&#34;&gt;Use Public Keys Instead of Passwords&lt;/h3&gt;&lt;p&gt;To only accept public-key authentication, disabling password andkeyboard-interactive authentication:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# ip ssh server algorithm authentication publickey&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Use an existing RSA key or generate a new one on your machine:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;linux$ C=&amp;#34;$(hostname)-$USER-remotehost-remoteuser-$(date -I)&amp;#34;linux$ ssh-keygen -t rsa -f ~/.ssh/&amp;#34;$C&amp;#34; -C &amp;#34;$C&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, import user&amp;rsquo;s public key into IOS. There is one thing to know, you cannotjust copy paste the entire RSA public key. IOS does not allow that amount ofinput. So you need to cut the public-key string up into bits of say 100characters each (&lt;a href=&#34;https://networklessons.com/miscellaneous/ssh-public-key-authentication-cisco-ios&#34;&gt;apparently, up to 254 characters&lt;/a&gt;). Yeah, not very handy:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# ip ssh pubkey-chainISR4321(conf-ssh-pubkey)# username stefanISR4321(conf-ssh-pubkey-user)# key-stringISR4321(conf-ssh-pubkey-data)# AAAAB3NzaC1yc2EAAAADAQABAAABgQCq8kjk8UYdDYco+U9glX74dm0x1AzBJO0Gnj9tfjcR29HJC1TxauyDuni0QIfkoqr8AMUISR4321(conf-ssh-pubkey-data)# ua8zl5h8iTbQe1ErBPBLyevTEfCgA+JOMx9RaBytSg8S5JCR+ur65zuBLU3AhrogtadJiprr6SBcgfIh5Ryrj/oqRzBD+WTskQTdGNeBBn5ISR4321(conf-ssh-pubkey-data)# ... etc ...ISR4321(conf-ssh-pubkey-data)# exit&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You could also specify the hash of your public key instead:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config)# ip ssh pubkey-chainISR4321(conf-ssh-pubkey)# username stefanISR4321(conf-ssh-pubkey-user)# key-hash ssh-rsa 7f7f72bfa276b87f44aaca363042c344 test&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On my (old) version of IOS (16.06.04), the &lt;code&gt;key-hash&lt;/code&gt; command takes a key-type(which can only be &lt;code&gt;ssh-rsa&lt;/code&gt;) and the hex-encoded MD5 digest of your public key.Lastly, you can enter an optional comment (&lt;code&gt;test&lt;/code&gt; in my case).&lt;/p&gt;&lt;p&gt;To calculate the hex-encoded MD5 digest of public key with &lt;code&gt;openssl&lt;/code&gt;, first cutthe base64-encoded public key from the public-key file (2nd field):&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cut -d &amp;#39; &amp;#39; -f2 &amp;lt; pub.key | \    openssl base64 -d | \    openssl dgst -md5MD5(stdin)= 7f7f72bfa276b87f44aaca363042c344&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After pasting the base64-encoded public key or entering the MD5 hash, you cannow authenticate with your public key:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ssh -l stefan -i ~/.ssh/yourkey 192.168.88.254&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;disabling-ssh&#34;&gt;Disabling SSH&lt;/h3&gt;&lt;p&gt;I am a big fan of disabling services I do not use. That decreases attack surfaceand is good security hygiene. I cannot find a way to disable the listeningsocket on port 22, but I can disable any kind of meaningful interaction on theport:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# conf tISR4321(config)# line vty 0 15ISR4321(config-line)# no transport input&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;IOS will now send a TCP FIN immediately when my SSH client starts to talk:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sudo tcpdump -nti eth1 port sshIP 192.168.88.252.38808 &amp;gt; 192.168.88.254.22: Flags [S], seq 1614430111, win 64240, options [mss 1460,sackOK,TS val 72081744 ecr 0,nop,wscale 7], length 0IP 192.168.88.254.22 &amp;gt; 192.168.88.252.38808: Flags [S.], seq 1208382352, ack 1614430112, win 4128, options [mss 1460], length 0IP 192.168.88.252.38808 &amp;gt; 192.168.88.254.22: Flags [.], ack 1, win 64240, length 0IP 192.168.88.252.38808 &amp;gt; 192.168.88.254.22: Flags [P.], seq 1:22, ack 1, win 64240, length 21: SSH: SSH-2.0-OpenSSH_9.9IP 192.168.88.254.22 &amp;gt; 192.168.88.252.38808: Flags [.], ack 22, win 4107, length 0IP 192.168.88.254.22 &amp;gt; 192.168.88.252.38808: Flags [FP.], seq 1, ack 22, win 4107, length 0IP 192.168.88.252.38808 &amp;gt; 192.168.88.254.22: Flags [F.], seq 22, ack 2, win 64239, length 0IP 192.168.88.254.22 &amp;gt; 192.168.88.252.38808: Flags [.], ack 23, win 4107, length 0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;no transport input&lt;/code&gt; &lt;em&gt;does&lt;/em&gt; disable telnet on port 23 entirely, though:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sudo tcpdump -nti eth1 port telnetIP 192.168.88.252.59455 &amp;gt; 192.168.88.254.23: Flags [S], seq 3152383467, win 1024, options [mss 1460], length 0IP 192.168.88.254.23 &amp;gt; 192.168.88.252.59455: Flags [R.], seq 0, ack 3152383468, win 0, length 0&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;obtain-servers-public-key&#34;&gt;Obtain Server&amp;rsquo;s Public Key&lt;/h3&gt;&lt;p&gt;On the client side, we need to know the server&amp;rsquo;s public key, so we can be surenobody is playing miscreant in the middle:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321#sh ip sshSSH Enabled - version 1.99Authentication methods:publickey,keyboard-interactive,passwordAuthentication Publickey Algorithms:x509v3-ssh-rsa,ssh-rsaHostkey Algorithms:x509v3-ssh-rsa,ssh-rsaEncryption Algorithms:aes128-ctr,aes192-ctr,aes256-ctrMAC Algorithms:hmac-sha2-256,hmac-sha2-512,hmac-sha1,hmac-sha1-96KEX Algorithms:diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1Authentication timeout: 120 secs; Authentication retries: 3Minimum expected Diffie Hellman key size : 2048 bitsIOS Keys in SECSH format(ssh-rsa, base64 encoded): TP-self-signed-3738675831ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCn62IP88wHq9NilUvTuQ2Min+1CZQ+N8NtFyamsNaPgPibo71xq9joiCUQQcdi/tZYqsUfsGQQJ05QXlDj81z2EEnqZ+a3GK7XjulbStWdY2innc+UA//gYrhj21lar4PSsuFjz2VoBYxEoloh/Mk4o068IjeMNWrV3zADgAyHwigfYJyNdpoDkIM/PMb0J1XKR65/cCJz710olSBTorYdrmLEVp+mGnnGUfVQr4xovVeU/Sze1foutzVEkds6E9Olxn6bCqHjbiIetLtwiCYDEyLytjPA2SIYtmltCi/TByLl36cR6tNj6PiFMgvb1UkjwiF1Cse6WvKLzxS9k0M3&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The line starting with &amp;ldquo;IOS Keys&amp;rdquo; is the server&amp;rsquo;s public key. If you copy it toa Unix box, you can calculate the SHA256 fingerprint as follows:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ssh-keygen -l -f isr4300.pub&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or using &lt;code&gt;openssl&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ openssl base64 -d &amp;lt; isr4300.pub | \    openssl dgst -sha256 -binary | \    openssl base64CRqX+T3a/48l6GT00Tgz3SmnkcNwlgt0B+vFI8VuzxI=&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Either paste this hash when &lt;code&gt;ssh(1)&lt;/code&gt; prompts to verify the remote host&amp;rsquo;sfingerprint (&lt;code&gt;SHA256:CRqX+T3a/48l6GT00Tgz3SmnkcNwlgt0B+vFI8VuzxI=&lt;/code&gt;) or store thepublic key in &lt;code&gt;~/.ssh/known_hosts&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;192.168.88.254 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCn62IP88wHq9NilUvTuQ2Min+1CZQ+N8NtFyamsNaPgPibo71xq9joiCUQQcdi/tZYqsUfsGQQJ05QXlDj81z2EEnqZ+a3GK7XjulbStWdY2innc+UA//gYrhj21lar4PSsuFjz2VoBYxEoloh/Mk4o068IjeMNWrV3zADgAyHwigfYJyNdpoDkIM/PMb0J1XKR65/cCJz710olSBTorYdrmLEVp+mGnnGUfVQr4xovVeU/Sze1foutzVEkds6E9Olxn6bCqHjbiIetLtwiCYDEyLytjPA2SIYtmltCi/TByLl36cR6tNj6PiFMgvb1UkjwiF1Cse6WvKLzxS9k0M3&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can connect to our Cisco device with the peace of mind that we are onlytalking to the device, and nobody else.&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&lt;hr&gt;&lt;ol&gt;&lt;li id=&#34;fn:1&#34;&gt;&lt;p&gt;Set &lt;code&gt;privilege 15&lt;/code&gt; to enter enable mode immediately after login. Set&lt;code&gt;algorithm-type scrypt&lt;/code&gt; to use a better PBKDF.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>How to Set Up Minicom for Serial Access</title>
       <link>https://relentlesscoding.com/posts/set-up-minicom-for-serial-access/</link>
       <pubDate>Mon, 10 Feb 2025 16:28:51 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/set-up-minicom-for-serial-access/</guid>
       <description>&lt;p&gt;If you want to connect to a networking device from Cisco or MikroTik, you getout-of-band access to the device over a serial console port. &lt;code&gt;minicom&lt;/code&gt; is one ofseveral programs that can be used to talk to these devices over a serialconnection.&lt;/p&gt;&lt;!-- more --&gt;&lt;blockquote&gt;&lt;p&gt;[A serial console port] enables administration of a machine even if it has nokeyboard, mouse, monitor, or network attached to it.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&#34;https://wiki.archlinux.org/title/Working_with_the_serial_console#Minicom&#34;&gt;Read Arch Linux wiki on working with the serial console&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&#34;edit-configuration&#34;&gt;Edit Configuration&lt;/h2&gt;&lt;p&gt;To set up &lt;code&gt;minicom&lt;/code&gt; enter:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ minicom -s&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;minicom&lt;/code&gt; can only write its configuration file under &lt;code&gt;/etc/minirc.dfl&lt;/code&gt; if youare root, though.&lt;/p&gt;&lt;p&gt;Under &amp;ldquo;Serial port setup&amp;rdquo;, set &amp;ldquo;Serial Device&amp;rdquo; to the /dev device file which isconnected to the router.&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/minicom-serial-port-setup.png&#34; alt=&#34;The minicom &amp;ldquo;Serial port setup&amp;rdquo; TUI screen. Under &amp;ldquo;A&amp;rdquo;, it shows the SerialDevice currently set to /dev/ttyACM0. Under &amp;ldquo;E&amp;rdquo;, it shows Bps/Par/Bits currentlyset to 115200 8N1.&#34;&gt;&lt;/p&gt;&lt;p&gt;When I plug in my RJ-45-rollover-USB-converter cable, it shows up under&lt;code&gt;/dev/ttyUSBn&lt;/code&gt; (where &lt;code&gt;n&lt;/code&gt; is a number). When I connect with my USB mini-B cable,it shows up under &lt;a href=&#34;https://en.wikipedia.org/wiki/USB_communications_device_class&#34;&gt;&lt;code&gt;/dev/ttyACMn&lt;/code&gt;&lt;/a&gt;. According to the Arch wiki, a traditionalhardware serial port, which, by default, modern computers do not ship withanymore, would show up under &lt;code&gt;/dev/ttySn&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Make sure your user can access that port, though. On my Arch installation,&lt;code&gt;/dev/ttyUSB0&lt;/code&gt; is owned by root and part of group &lt;code&gt;uucp&lt;/code&gt;. Add yourself to thegroup and restart your device or use &lt;code&gt;sudo -g uucp minicom&lt;/code&gt; afterwards:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# usermod -aG uucp stefan&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Set &lt;code&gt;Bps/Par/Bits&lt;/code&gt; to &lt;code&gt;9600 8N1&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;increase-text-output-rate&#34;&gt;Increase Text Output Rate&lt;/h2&gt;&lt;p&gt;To make text appear on the screen faster, increase the &lt;a href=&#34;https://en.wikipedia.org/wiki/Baud&#34;&gt;baud rate&lt;/a&gt; in IOS:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321# conf tISR4321(config)# line console 0ISR4321(config-line)# speed 115200&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, press &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;P&lt;/kbd&gt; in &lt;code&gt;minicom&lt;/code&gt; and press &lt;kbd&gt;E&lt;/kbd&gt; toset the speed to 115200.&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/minicom-communication-parameters.png&#34; alt=&#34;The minicom &amp;ldquo;Comm Parameters&amp;rdquo; TUI screen. It has a heading saying &amp;ldquo;Current:115200 8N1&amp;rdquo;. There is a subheading &amp;ldquo;Speed&amp;rdquo; where under &amp;ldquo;E&amp;rdquo; it says115200.&#34;&gt;&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;</description>
     </item>
   
     <item>
       <title>Configure Static DNS Entries on Cisco IOS</title>
       <link>https://relentlesscoding.com/posts/cisco-ios-set-static-dns-entries/</link>
       <pubDate>Mon, 10 Feb 2025 16:21:42 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/cisco-ios-set-static-dns-entries/</guid>
       <description>&lt;p&gt;You can configure Cisco&amp;rsquo;s IOS to remember IP addresses for host, without havingto resort to a DNS lookup. Very much indeed like adding an entry to your&lt;code&gt;/etc/hosts&lt;/code&gt; file.&lt;/p&gt;&lt;!-- more --&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Router(config)# ip host example.invalid 192.168.1.1Router(config)# endRouter# sh hostDefault Domain is example.invalidName/address lookup uses domain serviceName servers are 255.255.255.255Codes: UN - unknown, EX - expired, OK - OK, ?? - revalidate       temp - temporary, perm - permanent       NA - Not Applicable None - Not definedHost             Port  Flags       Age  Type  Address(es)example.invalid  None  (perm, OK)  0    IP    192.168.1.1&lt;/code&gt;&lt;/pre&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;</description>
     </item>
   
     <item>
       <title>Made a Typo? IOS Will Make You Wait for DNS Resolution</title>
       <link>https://relentlesscoding.com/posts/cisco-ios-typo-dns-resolution/</link>
       <pubDate>Mon, 10 Feb 2025 15:53:38 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/cisco-ios-typo-dns-resolution/</guid>
       <description>&lt;p&gt;Another one of those Cisco IOS gotchas: when mistyping a command, you have towait a minute before you get back a prompt. How to get back your prompt rightaway?&lt;/p&gt;&lt;!-- more --&gt;&lt;p&gt;To beginners with fat fingers like me, this happens all the time:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Router&amp;gt; fooTranslating &amp;#34;foo&amp;#34;...domain server (255.255.255.255)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;IOS interprets this as a hostname to Telnet into. It will make you &lt;a href=&#34;https://www.certskills.com/clab134/#1621538143010-3327296b-3256&#34;&gt;wait a minutebefore you can continue&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;If the router has no DNS servers configured, another IOS default causes therouter to broadcast on the connected subnets looking for a DNS server toresolve names – with around a one-minute timeout waiting for a response.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;C&lt;/kbd&gt; does nothing, but instead, we have to press&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;^&lt;/kbd&gt; to quickly cancel that command:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Router&amp;gt; fooTranslating &amp;#34;foo&amp;#34;...domain server (255.255.255.255) % Name lookup aborted&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Type &lt;code&gt;no ip domain-lookup&lt;/code&gt; in global configuration mode to disable all DNSlookups. I do not currently know if there are any downsides to this.&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;</description>
     </item>
   
     <item>
       <title>Improve Annoying Console Logging of Cisco IOS</title>
       <link>https://relentlesscoding.com/posts/cisco-ios-improve-annoying-console-logging/</link>
       <pubDate>Mon, 10 Feb 2025 15:41:55 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/cisco-ios-improve-annoying-console-logging/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s have a quick look at how to keep Cisco IOS from interfering with yourprompt?&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;improve-annoying-console-logging&#34;&gt;Improve Annoying Console Logging&lt;/h2&gt;&lt;p&gt;I have set &lt;code&gt;logging console&lt;/code&gt; to &lt;code&gt;notifications&lt;/code&gt; or lower. Now, when exitingglobal configuration mode with ^Z or &lt;code&gt;end&lt;/code&gt; and immediately starting to typeanother command such as &lt;code&gt;sh run&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321(config-line)#end ISR4321#*Mar  9 09:27:18.751: %SYS-5-CONFIG_I: Configured from console by consolesh run&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The logging interferes with what I am typing. Nothing too bad, I can type ^L toquickly get my prompt on a new line, but it would be convenient if IOS would dothat for me. Turns out, you can tell IOS to wait for the output of a &lt;code&gt;show&lt;/code&gt;command to finish before printing anything on the console. This will also put ahalf-typed command on its own line, instead of appended to the console line.Much better.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ISR4321#conf tEnter configuration commands, one per line.  End with CNTL/Z.ISR4321(config)#line con 0ISR4321(config-line)#logging synchronousISR4321(config-line)#endISR4321#*Mar  9 09:29:08.489: %SYS-5-CONFIG_I: Configured from console by consoleISR4321#sh run&lt;/code&gt;&lt;/pre&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;</description>
     </item>
   
     <item>
       <title>STP</title>
       <link>https://relentlesscoding.com/posts/cisco-ccna-mikrotik-stp/</link>
       <pubDate>Mon, 10 Feb 2025 10:42:13 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/cisco-ccna-mikrotik-stp/</guid>
       <description>&lt;p&gt;In this post, we will have a look at the venerable Spanning Tree Protocol (STP),what it is used for, and how to configure and verify it on Cisco IOS andMikroTik.&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;stp-theory&#34;&gt;STP Theory&lt;/h2&gt;&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Spanning_Tree_Protocol&#34;&gt;STP (802.1D)&lt;/a&gt; prevents switching loops. Without it, a LAN with redundantlinks is susceptible to:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Broadcast storms, resulting in&lt;/li&gt;&lt;li&gt;MAC table instability; and&lt;/li&gt;&lt;li&gt;Multiple frame transmission&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;A &lt;strong&gt;broadcast storm&lt;/strong&gt; is caused by a broadcast frame that is put on a LAN thathas a loop. It never dies, because it is flooded out every interface of aswitch, except the interface it came in on.&lt;/p&gt;&lt;p&gt;Because the same broadcast frame comes in on different ports of a switch withthe same source address, the switch keeps updating its MAC table: the entry isflapping.&lt;/p&gt;&lt;p&gt;Another consequence of a broadcast storm is that the same frame will bedelivered multiple times to the destination, and it might just be that theapplication cannot handle that correctly.&lt;/p&gt;&lt;p&gt;STP makes sure there is only path from a switch to a so-called root bridge. Theroot bridge gets elected among all available switches in the broadcast domain.&lt;/p&gt;&lt;h2 id=&#34;root-bridge-election&#34;&gt;Root Bridge Election&lt;/h2&gt;&lt;p&gt;The election works as follows: using a so-called Hello configuration &lt;a href=&#34;https://en.wikipedia.org/wiki/Spanning_Tree_Protocol#Bridge_protocol_data_units&#34;&gt;BridgeData Protocol Units (BPDUs)&lt;/a&gt;, each switch advertises itself as the rootbridge to its neighbors. It does this by including a root and sender Bridge ID(BID) that consists of a 2-byte priority field and a 6-byte system identifier:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/dissected-bid.png&#34; alt=&#34;Shows a Bridge ID dissected into a 2-byte priority value and a 6-byte MACaddress&#34;&gt;&lt;/p&gt;&lt;p&gt;A lower priority value indicates higher priority. The system identifier is equalto the switch&amp;rsquo;s burned-in MAC address.&lt;/p&gt;&lt;p&gt;The Hello BPDU includes the following fields:&lt;/p&gt;&lt;table&gt;  &lt;thead&gt;      &lt;tr&gt;          &lt;th style=&#34;text-align: left&#34;&gt;Field&lt;/th&gt;          &lt;th style=&#34;text-align: left&#34;&gt;Description&lt;/th&gt;      &lt;/tr&gt;  &lt;/thead&gt;  &lt;tbody&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Root BID&lt;/strong&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;BID of switch the sender of this Hello BPDU believes to be the root bridge&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Sender&amp;rsquo;s BID&lt;/strong&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;BID of switch sending this Hello BPDU&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Sender&amp;rsquo;s root cost&lt;/strong&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;Root path cost between the sending switch and the current root&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Root&amp;rsquo;s timer values&lt;/strong&gt;&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;Hello timer, MaxAge timer, forward delay timer&lt;/td&gt;      &lt;/tr&gt;  &lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;A neighbor receives this BPDU and looks at the advertised root BID. If the rootBID is &lt;em&gt;superior&lt;/em&gt; (meaning the priority is higher than what it currently thinksof as the root), it will:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;change the sender BID in the Hello BPDU to its own BID&lt;/li&gt;&lt;li&gt;change the sender&amp;rsquo;s root cost to the cost between this switch and the root;and&lt;/li&gt;&lt;li&gt;forward the modified Hello BPDU out its other ports.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If the priority is lower (&lt;em&gt;inferior&lt;/em&gt;), however, it will just ignore the BPDU.&lt;/p&gt;&lt;p&gt;By default, the 2-byte priority field in the BID is equal to 32,768 (or 0x8000in hex). In that case, the system ID (the burned-in MAC address) is used as atie breaker: the lower the 48-bit MAC address value, the higher its priority(&lt;code&gt;1111.1111.1111&lt;/code&gt; wins from &lt;code&gt;2222.2222.2222&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;After a while, the root will be elected and the network will start to&lt;a href=&#34;https://en.wikipedia.org/wiki/Convergence_(routing)&#34;&gt;converge&lt;/a&gt; (= stabilize on a network topology).&lt;/p&gt;&lt;h2 id=&#34;stp-roles-and-states&#34;&gt;STP Roles and States&lt;/h2&gt;&lt;p&gt;After the election, each switch&amp;rsquo;s interface takes on a role. These roles can be:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Root Port (RP)&lt;/strong&gt;: the single port on a switch facing the root switch thathas the least cost path to the root switch. Root ports are chosen by:&lt;ol&gt;&lt;li&gt;root path cost&lt;/li&gt;&lt;li&gt;BID of the upstream device&lt;/li&gt;&lt;li&gt;bridge port ID of the upstream device (consisting of a port priority and aunique ID, such as &lt;code&gt;128.1&lt;/code&gt;)&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Designated Port (DP)&lt;/strong&gt;: a port (facing away from the root switch) on asegment that has the least cost path to the root switch. This port willforward the Hello BPDU onto the segment.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Because the elected root switch has the lowest cost path (0) to the root on allits ports, all of its ports will be designated ports (DPs).&lt;/p&gt;&lt;p&gt;The cost of a path to the root is determined by looking at the current speed(not maximum speed!) of the links that lie between a non-root switch&amp;rsquo;s port andthe root switch&amp;rsquo;s port. By default, traversing a 100 Mbps link carries a cost of19, while a 1000 Mbps costs 4:&lt;/p&gt;&lt;table&gt;  &lt;thead&gt;      &lt;tr&gt;          &lt;th style=&#34;text-align: left&#34;&gt;Ethernet Speed&lt;/th&gt;          &lt;th style=&#34;text-align: left&#34;&gt;IEEE Cost 1998 (short)&lt;/th&gt;          &lt;th style=&#34;text-align: left&#34;&gt;IEEE Cost 2004 (long)&lt;/th&gt;      &lt;/tr&gt;  &lt;/thead&gt;  &lt;tbody&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: left&#34;&gt;10 Mpbs&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;100&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;2,000,000&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: left&#34;&gt;100 Mpbs&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;19&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;200,000&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: left&#34;&gt;1,000 Mpbs/1 Gbps&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;4&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;20,000&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: left&#34;&gt;10,000 Mpbs/10 Gbps&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;2&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;2,000&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: left&#34;&gt;100,000 Mpbs/100 Gbps&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;N/A&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;200&lt;/td&gt;      &lt;/tr&gt;      &lt;tr&gt;          &lt;td style=&#34;text-align: left&#34;&gt;1,000 Gbps/1 Tbps&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;N/A&lt;/td&gt;          &lt;td style=&#34;text-align: left&#34;&gt;20&lt;/td&gt;      &lt;/tr&gt;  &lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;On IOS, you can switch between the 2 short and long methods by:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Switch# spanning-tree pathcost method &amp;lt;short|long&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Set the interface cost for an interface:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Switch(config-if)# spanning-tree vlan 1 cost 123&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that on IOS, you set the cost &lt;em&gt;per VLAN&lt;/em&gt; on an interface. That is becauseIOS uses a modified version of STP called the &lt;a href=&#34;https://en.wikipedia.org/wiki/Spanning_Tree_Protocol#PVST&#34;&gt;Per-VLAN Spanning Tree Protocol(PVST+)&lt;/a&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, which is a topic for a separate blog post.&lt;/p&gt;&lt;p&gt;With STP, every interface can be in state:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Blocking&lt;/strong&gt; (stable)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Listening&lt;/strong&gt; (transient)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Learning&lt;/strong&gt; (transient)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Forwarding&lt;/strong&gt; (stable)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Disabled&lt;/strong&gt; (stable)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;(Between parentheses whether the state is a stable state or an intermediatestate towards a stable state.)&lt;/p&gt;&lt;p&gt;Root Ports and Designated Ports will be forwarding frames. If a port is not aRP or a DP, it will be blocked. That means that it will not receive nor transmituser frames. It will still receive BPDUs, though.&lt;/p&gt;&lt;p&gt;If an interface is administratively shut down, its state will be &amp;ldquo;disabled&amp;rdquo;.&lt;/p&gt;&lt;p&gt;On IOS, you can check on all of this with &lt;code&gt;show spanning-tree&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Switch#sh spanning-tree VLAN0001  Spanning tree enabled protocol rstp  Root ID    Priority    24577             Address     000B.BE6A.4623             Cost        19             Port        1(FastEthernet0/1)             Hello Time  2 sec  Max Age 20 sec  Forward Delay 15 sec  Bridge ID  Priority    28673  (priority 28672 sys-id-ext 1)             Address     00D0.BA39.4B87             Hello Time  2 sec  Max Age 20 sec  Forward Delay 15 sec             Aging Time  20Interface        Role Sts Cost      Prio.Nbr Type---------------- ---- --- --------- -------- ---------------------------Fa0/2            Desg FWD 19        128.2    P2pFa0/1            Root FWD 19        128.1    P2p&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;timers&#34;&gt;Timers&lt;/h2&gt;&lt;p&gt;A root bridge sends a Hello BPDU every 2 seconds by default. This Hello Time iscarried in the BPDU. It also contains a value for MaxAge, which defines how longswitches should wait after ceasing to hear Hellos before trying to change theSTP topology. By default, it is 10 times the Hello Time. So a switch will wait20 seconds for Hellos to come in before taking action.&lt;/p&gt;&lt;p&gt;The forward delay timer is used to determine how long a switch will stay inlistening and learning states after deciding to transition a port from blockingto listening.&lt;/p&gt;&lt;p&gt;In the listening state, the switch processes BPDUs but does not forward any userframes. It tries to determine whether the interface should indeed be forwardingor should return to the blocking state. It makes this decision based on thereceived BPDUs. Also, the switch removes unused MAC table entries for which noframes are received during this period.&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; Inthe learning state, the switch begins to learn the MAC addresses of framesreceived on the interface.&lt;/p&gt;&lt;p&gt;All in all, a switch could take 50 seconds (20 MaxAge, 2 times the forward delayfor listening and learning) before an interface transitions from blocking toforwarding.&lt;/p&gt;&lt;h2 id=&#34;edge-devices&#34;&gt;Edge Devices&lt;/h2&gt;&lt;p&gt;Only switches participate in STP. Edge devices (PCs, laptops, IoT, etc.) do not.But the links that connect edge devices to switches will, by default, still gothrough the motions by first entering listening state and then learning state.You can short-circuit this process by configuring &lt;code&gt;spanning-tree portfast&lt;/code&gt; onthe interface. The link will now transition to forwarding immediately.&lt;/p&gt;&lt;p&gt;In the event you do connect a switch to a port that was configured with&lt;code&gt;portfast&lt;/code&gt;, you can inadvertently create a loop. In that case, you should alsoenable BPDU Guard on the port by setting &lt;code&gt;spanning-tree bpduguard enable&lt;/code&gt;. Ifthe port detects a BPDU coming in, it will automatically disable itself.&lt;/p&gt;&lt;h2 id=&#34;mikrotik&#34;&gt;MikroTik&lt;/h2&gt;&lt;p&gt;On &lt;a href=&#34;https://help.mikrotik.com/docs/spaces/ROS/pages/21725254/Spanning+Tree+Protocol&#34;&gt;MikroTik&amp;rsquo;s RouterOS 7&lt;/a&gt;, make sure you have STP enabled:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[user@rb] /interface/bridge/set BR1 protocol-mode=rstp&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(Even though this post is about the traditional &lt;a href=&#34;https://en.wikipedia.org/wiki/IEEE_802.1D&#34;&gt;IEEE 802.1D STP&lt;/a&gt;,please enable the more modern (faster!) RSTP instead.)&lt;/p&gt;&lt;p&gt;Check the status of a bridge:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[u@rb] /interface/bridge/monitor BR1                  state: enabled                     current-mac-address: 48:A9:8A:11:11:11            root-bridge: yes                              root-bridge-id: 0x8000.48:A9:8A:11:11:11         root-path-cost: 0                                     root-port: none                                 port-count: 23                        designated-port-count: 5                                  fast-forward: no&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It indicates this bridge is the root bridge (&lt;code&gt;current-mac-address == root-bridge-id&lt;/code&gt;) and has a BID of &lt;code&gt;0x8000.48:A9:8A:11:11:11&lt;/code&gt;: here, &lt;code&gt;0x8000&lt;/code&gt; isthe priority in hex (= 32,768 in decimal; change with &lt;code&gt;set BR1 priority=0xNNNN&lt;/code&gt;to a multiple of 4,096) and the system ID is the 48:A9:8A:11:11:11.&lt;/p&gt;&lt;p&gt;Check an individual port:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[u@rb] /interface/bridge/port&amp;gt; monitor [find interface=ether17]            interface: ether17               status: in-bridge                port-number: 16                              role: designated-port            edge-port: yes              edge-port-discovery: yes              point-to-point-port: yes                     external-fdb: no                      sending-rstp: yes                         learning: yes                       forwarding: yes                 actual-path-cost: 10                  hw-offload-group: switch1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It automatically discovered that interface &lt;code&gt;ether17&lt;/code&gt; is connected to an edgedevice. If you want to be specific, you can set PortFast by &lt;code&gt;set [find interface=ether17] edge=yes&lt;/code&gt;. Set BPDU Guard with &lt;code&gt;set [find interface=ether1] bpdu-guard=yes&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Change between the long and short automatic costs based on interfaces speeds:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[user@rb] /interface/bridge/set BR1 port-cost-mode=long&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And a bit of warning: RouterOS does not currently play well with &lt;a href=&#34;https://en.wikipedia.org/wiki/Spanning_Tree_Protocol#PVST&#34;&gt;Cisco&amp;rsquo;sPVST&lt;/a&gt;. It will treat it as regular multicast traffic.&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&lt;hr&gt;&lt;ol&gt;&lt;li id=&#34;fn:1&#34;&gt;&lt;p&gt;The &lt;code&gt;+&lt;/code&gt; at the end indicates that &lt;a href=&#34;https://en.wikipedia.org/wiki/IEEE_802.1Q&#34;&gt;IEEE 802.1Q&lt;/a&gt; is used fortrunking instead of Cisco&amp;rsquo;s older, proprietary &lt;a href=&#34;https://en.wikipedia.org/wiki/Cisco_Inter-Switch_Link&#34;&gt;ISL&lt;/a&gt; protocol.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:2&#34;&gt;&lt;p&gt;&lt;a href=&#34;https://web.archive.org/web/20250322105432/https://www.certskills.com/stp-do-we-really-need-a-listening-state/&#34;&gt;This state was subject to dispute, asit is unclear whether it matters if STP is learning MAC addresses or nothere.&lt;/a&gt; It is telling that RSTP has done away with this state.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Check OCSP With OpenSSL CLI Tool</title>
       <link>https://relentlesscoding.com/posts/check-ocsp-with-openssl-cli/</link>
       <pubDate>Sat, 08 Feb 2025 12:04:49 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/check-ocsp-with-openssl-cli/</guid>
       <description>&lt;p&gt;How to query an OCSP responder to check the validity of a certificate?&lt;/p&gt;&lt;!-- more --&gt;&lt;p&gt;Get the cert chain:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nv&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;github.com&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; Q &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    openssl s_client -showcerts -servername &lt;span class=&#34;nv&#34;&gt;$H&lt;/span&gt; -host &lt;span class=&#34;nv&#34;&gt;$H&lt;/span&gt; -port &lt;span class=&#34;m&#34;&gt;443&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;BEGIN{n=0}; /-BEGIN/,/-END/{ print &amp;gt;&amp;gt; n &amp;#34;.pem&amp;#34; }; /-END/{n++}&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://man.archlinux.org/man/openssl.1ssl&#34;&gt;&lt;code&gt;-showcerts&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Displays the server certificate list as sent by the server: it only consistsof certificates the server has sent (in the order the server has sent them).It is &lt;strong&gt;not&lt;/strong&gt; a verified chain.&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;I use &lt;code&gt;awk(1)&lt;/code&gt; to filter the PEM certificates from the other output. It sets acounter to &lt;code&gt;0&lt;/code&gt;. It then appends every line between &lt;code&gt;-----BEGIN CERTIFICATE-----&lt;/code&gt; and &lt;code&gt;-----END CERTIFICATE-----&lt;/code&gt; to a file. It increments thenumber after every &lt;code&gt;-----END CERTIFICATE-----&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;This will create as many files as the certificate chain is long. The file&lt;code&gt;0.pem&lt;/code&gt; contains the &amp;ldquo;certificate at depth 0&amp;rdquo;: the leaf certificate. The file&lt;code&gt;1.pem&lt;/code&gt; contains the &amp;ldquo;certificate at depth 1&amp;rdquo;: the issuer of the leafcertificate. Caveat: this &lt;em&gt;may&lt;/em&gt; not be true, as the order depends on theserver. Check the subject and issuer of each certificate and rename the filesaccordingly:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; f in *.pem&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      openssl x509 -in &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; -noout -subject -issuer&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;When &lt;code&gt;openssl s_client&lt;/code&gt; receives &lt;code&gt;Q&lt;/code&gt;, it tears down the TLS connection to theremote server.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Get the OCSP responder URI from the leaf cert:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; openssl x509 -in 0.pem -noout -ocsp_uri&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;http://ocsp.sectigo.com&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s store it in a variable for easier access:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ocsp_uri&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;openssl x509 -in 0.pem -noout -ocsp_uri&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We now have everything we need:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; openssl ocsp -issuer 1.pem -cert 0.pem -url &lt;span class=&#34;nv&#34;&gt;$ocsp_uri&lt;/span&gt; -text&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;OCSP Request Data:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    Version: 1 (0x0)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    Requestor List:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;        Certificate ID:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;          Hash Algorithm: sha1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;          Issuer Name Hash: CF94DC5C304AA79485721F956E67895AC21657DD&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;          Issuer Key Hash: F6850A3B1186E1047D0EAA0B2CD2EECC647B7BAE&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;          Serial Number: AB6686B5627BE80596821330128649F5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    Request Extensions:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;        OCSP Nonce:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;            041038CA4568E928AE97DD06259DBF6CBE0A&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;OCSP Response Data:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    OCSP Response Status: successful (0x0)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    Response Type: Basic OCSP Response&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    Version: 1 (0x0)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    Responder Id: F6850A3B1186E1047D0EAA0B2CD2EECC647B7BAE&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    Produced At: Feb  7 06:04:44 2025 GMT&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    Responses:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    Certificate ID:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;      Hash Algorithm: sha1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;      Issuer Name Hash: CF94DC5C304AA79485721F956E67895AC21657DD&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;      Issuer Key Hash: F6850A3B1186E1047D0EAA0B2CD2EECC647B7BAE&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;      Serial Number: AB6686B5627BE80596821330128649F5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    Cert Status: good&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    This Update: Feb  7 06:04:44 2025 GMT&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    Next Update: Feb 14 06:04:43 2025 GMT&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;go&#34;&gt;    Signature Algorithm: ecdsa-with-SHA256&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    Signature Value:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;        30:44:02:20:08:b0:f7:00:53:f9:d4:6c:7f:a0:91:ac:0a:91:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;        af:a1:34:e2:c5:2c:dc:14:a0:1c:de:e9:be:22:6f:25:d8:2f:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;        02:20:0d:09:7e:57:43:59:ce:c4:65:f1:4b:f9:3d:f4:46:82:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;        78:01:4c:53:ca:2f:45:b3:35:0c:83:54:53:f0:68:ed&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;WARNING: no nonce in response&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Response verify OK&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;0.pem: good&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;        This Update: Feb  7 06:04:44 2025 GMT&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;        Next Update: Feb 14 06:04:43 2025 GMT&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;In the request and response, the &amp;ldquo;Hash Algorithm&amp;rdquo; indicates SHA-1 was used togenerate hashes.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The serial number is the serial number of the leaf cert:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; openssl x509 -in 0.pem -noout -serial&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;serial=AB6686B5627BE80596821330128649F5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The &amp;ldquo;Issuer Name Hash&amp;rdquo; and &amp;ldquo;Issuer Key Hash&amp;rdquo;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;issuerNameHash&lt;/code&gt; is the hash of the issuer&amp;rsquo;s distinguished name(DN). The hash shall be calculated over the DER encoding of theissuer&amp;rsquo;s name field in the certificate being checked.&lt;/p&gt;&lt;p&gt;&lt;code&gt;issuerKeyHash&lt;/code&gt; is the hash of the issuer&amp;rsquo;s public key. The hashshall be calculated over the value (excluding tag and length) ofthe subject public key field in the issuer&amp;rsquo;s certificate.&lt;/p&gt;&lt;p&gt;&amp;ndash;&lt;cite&gt;&lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1&#34;&gt;RFC 6960&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This is hard to do by hand, so we use &lt;code&gt;openssl(1)&lt;/code&gt; once more:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; openssl x509 -in 1.pem -noout -ocspid&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Subject OCSP hash: CF94DC5C304AA79485721F956E67895AC21657DD&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Public key OCSP hash: F6850A3B1186E1047D0EAA0B2CD2EECC647B7BAE&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that these values correspond to what is in our OCSP request and response.&lt;/p&gt;&lt;p&gt;(I did not get output from LibreSSL 4.0.0 with &lt;code&gt;-ocspid&lt;/code&gt;, even though theoption is mentioned in its manpage.)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&amp;ldquo;WARNING: no nonce in response&amp;rdquo; means that the server did not include ournonce in their response. Nonces are used to prevent replay attacks. Theyincrease the load on the OCSP responder, however. That is why responders maychoose to ignore the nonce and return a canned response.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;see-also&#34;&gt;See Also&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc6960&#34;&gt;RFC 6960&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://man.archlinux.org/man/openssl.1ssl&#34;&gt;&lt;code&gt;openssl(1)&lt;/code&gt;&lt;/a&gt; version 3.4.0&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://man.archlinux.org/man/extra/libressl/libressl-openssl.1.en&#34;&gt;&lt;code&gt;libressl-openssl(1)&lt;/code&gt;&lt;/a&gt; version 4.0.0&lt;/li&gt;&lt;/ul&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai: --&gt;</description>
     </item>
   
     <item>
       <title>Rapid Spanning Tree Protocol (RSTP)</title>
       <link>https://relentlesscoding.com/posts/rstp/</link>
       <pubDate>Sun, 02 Feb 2025 16:36:16 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/rstp/</guid>
       <description>&lt;p&gt;&lt;a href=&#34;https://relentlesscoding.com/posts/cisco-ccna-mikrotik-stp/&#34;&gt;I previously discussed the plain-old Spanning Tree Protocol (STP)&lt;/a&gt;. In thispost, I will look at the differences introduces by its successor, the &lt;a href=&#34;https://en.wikipedia.org/wiki/Spanning_Tree_Protocol#Rapid_Spanning_Tree_Protocol&#34;&gt;RapidSpanning Tree Protocol (RSTP)&lt;/a&gt;.&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;the-case-for-rstp&#34;&gt;The Case for RSTP&lt;/h2&gt;&lt;p&gt;STP required 50 seconds to get a port in forwarding state: it waits 20 secondsfor the MaxAge (by default 10 times the value of the Hello Timer) timer toexpire. At that point, it knows it has to change the topology. It will spend theForward Delay in a listening state (15 seconds by default) and then anotherForward Delay in learning mode.&lt;/p&gt;&lt;p&gt;All that waiting is tedious: 50 seconds is a long time to be without networkconnectivity. Devices that live off of edge ports expect to be able to do DHCPand are unable to. Apparently, some DHCP client implementations just give up ifthey cannot Discover a DHCP server within those 50 seconds.&lt;/p&gt;&lt;p&gt;So RSTP promises faster convergence. It does this by introducing new roles. STPhad Root Ports (ports facing toward the root bridge that had the least pathcost from that port to the root) and Designated Ports (ports facing away fromthe root bridge that had the least path cost to the root bridge on that segmentand that was therefore designated to put the BPDUs on the segment).&lt;/p&gt;&lt;p&gt;RSTP introduces the &lt;strong&gt;Alternate&lt;/strong&gt; and &lt;strong&gt;Backup Port roles&lt;/strong&gt;. Alternate Portsfunction as secondaries to the Root Ports (on a &lt;strong&gt;point-to-point&lt;/strong&gt; link type).They have the least cost path to the root after the Root Port. Backup Ports areonly relevant to shared links. This means that when you have multiple links intothe same segment (the same collision domain), all but one of those links will beblocking (or &lt;em&gt;discarding&lt;/em&gt; in RSTP parlance). One of the blocked ports will be aBackup Port. Shared access to a collision domain only happens when usinghubs&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; (but see below for an interesting inter-op case with MikroTik bridges).&lt;/p&gt;&lt;p&gt;So: Alternate Ports step in for failing Root Ports, Backup Ports step in forfailing Designated Ports.&lt;/p&gt;&lt;p&gt;The idea of assigning Alternate Ports is that when a Root Port goes down, theAlternate Port can transition to the forwarding state without any delays. Samewith Backup Ports.&lt;/p&gt;&lt;p&gt;STP had 5 states a port could be in:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Blocking&lt;/li&gt;&lt;li&gt;Listening&lt;/li&gt;&lt;li&gt;Learning&lt;/li&gt;&lt;li&gt;Forwarding&lt;/li&gt;&lt;li&gt;Disabled&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;RSTP simplifies this to 3:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Discarding&lt;/li&gt;&lt;li&gt;Learning&lt;/li&gt;&lt;li&gt;Forwarding&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The discarding state now encapsulates the blocking and disabled states. Thelistening state is simply not used anymore.&lt;/p&gt;&lt;h2 id=&#34;key-differences-between-stp-and-rstp&#34;&gt;Key Differences between STP and RSTP&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;RSTP detects root failure in 3 Hello times (by default: &lt;code&gt;3 * 2 = 6&lt;/code&gt; seconds),instead of ten Hello times (by default: &lt;code&gt;10 * 2 = 20&lt;/code&gt; seconds).&lt;/li&gt;&lt;li&gt;RSTP may send BPDUs in the direction of the root bridge, confirming to theupstream bridge that it has received superior information. The bridgereceiving this confirmation can rapidly transition the port to the forwardingstate, skipping the listening/learning states.&lt;/li&gt;&lt;/ul&gt;&lt;!-- vi: se tw=80 cc=73 sw=0 ts=4 et ai spell: --&gt;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&lt;hr&gt;&lt;ol&gt;&lt;li id=&#34;fn:1&#34;&gt;&lt;p&gt;And, as everybody keeps saying in instruction videos and podcasts, theseare exceedingly rare these days. I&amp;rsquo;ve never even seen a hub in my lifeexcept on network diagrams.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>IEEE RSTP and Cisco RPVST&#43; Interop</title>
       <link>https://relentlesscoding.com/posts/ieee-rstp-cisco-rpvst-interop/</link>
       <pubDate>Sat, 01 Feb 2025 11:35:18 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/ieee-rstp-cisco-rpvst-interop/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s have a look at what happens when we plug some MikroTik devices into aCisco switch and string them together in a loop topology. What happens when RSTPand RPVST+ need to work together? Do we get a broadcast storm? Why (not)?&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;interaction-of-rstp-with-rpvst&#34;&gt;Interaction of RSTP with RPVST+&lt;/h2&gt;&lt;p&gt;RSTP is VLAN-agnostic. The 12-bit System ID Extension in the BID is set to allzeroes &lt;code&gt;0000.0000.0000&lt;/code&gt;. Cisco&amp;rsquo;s proprietary (R)PVST+, on the other hand,populates this field with the ID of the VLAN on which the BPDU containing theBID is broadcast.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/dissected-bid.png&#34; alt=&#34;Show the dissected BPDU BID: a 4-bit priority field, the System ID Extension(VLAN ID) and the System ID (MAC address).&#34; title=&#34;BPDU BID&#34;&gt;&lt;/p&gt;&lt;p&gt;RSTP sends untagged multicasts to &lt;code&gt;0180.C200.0000&lt;/code&gt; onto the native VLAN,creating a single Common Spanning Tree (CST). Cisco&amp;rsquo;s RPVST+ sends taggedmulticast frames to &lt;code&gt;0100.0CCC.CCCD&lt;/code&gt; on every VLAN, creating a separate tree forevery VLAN. So, if you have 10 VLANs defined, RPVST+ will send 10 BPDU Hellosevery Hello interval.&lt;/p&gt;&lt;p&gt;This also means that Cisco and MikroTik gear do not play (well) together.&lt;a href=&#34;https://help.mikrotik.com/docs/spaces/ROS/pages/21725254/Spanning+Tree+Protocol&#34;&gt;MikroTik treats Cisco&amp;rsquo;s proprietary (R)PVST+ frames as it would any othermulticast&lt;/a&gt; and forwards it out all ports except the port it received theframes on. Cisco will discard multicast traffic sent to the IEEE multicastaddress if VLAN 1 is not the native VLAN and not allowed on the trunk.&lt;/p&gt;&lt;p&gt;I found &lt;a href=&#34;https://learningnetwork.cisco.com/s/question/0D53i00000Kt1f1CAB/pvst-and-common-stp-native-vlan-operations&#34;&gt;the following image on the Cisco Community&lt;/a&gt;: it helps explain thesituation when the native VLAN is the default &lt;code&gt;1&lt;/code&gt; and when it is something else:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/rstp-cisco-native-vlan-1.webp&#34; alt=&#34;Flow chart explaining the difference in Cisco BPDU processing when native VLANID is 1 and when it is something else&#34;&gt;&lt;/p&gt;&lt;p&gt;This is the topology we will use to think about different scenario&amp;rsquo;s:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/rstp-reference-scenario.webp&#34; alt=&#34;Image of reference topology used in different scenario&amp;rsquo;sbelow&#34;&gt;&lt;/p&gt;&lt;p&gt;I won&amp;rsquo;t repeat the interface names (&lt;code&gt;ether5&lt;/code&gt;) in the scenario topologies,because for MikroTiks, they are pretty much meaningless. Plus the maximum speedsof the interfaces can be deduced from looking at the path cost. All paths costsare in the traditional IEEE 1998 form (see &lt;a href=&#34;https://relentlesscoding.com/posts/cisco-ccna-mikrotik-stp/&#34;&gt;this post&lt;/a&gt;) where 100 Mbps linkshave a path cost of 19 and 1 Gbps links a cost of 4.&lt;/p&gt;&lt;p&gt;The MikroTiks, a &lt;a href=&#34;https://mikrotik.com/product/RB750Gr3&#34;&gt;hEX&lt;/a&gt; and &lt;a href=&#34;https://mikrotik.com/product/RB941-2nD&#34;&gt;hAP lite&lt;/a&gt;, run 7.19.1 firmware. The Cisco, a&lt;a href=&#34;https://www.cisco.com/c/en/us/products/collateral/switches/catalyst-3650-series-switches/data_sheet-c78-729449.html&#34;&gt;3650C&lt;/a&gt;, runs a venerable 15.0(2)SE2.&lt;/p&gt;&lt;h3 id=&#34;scenario-1-everybody-uses-native-vlan-1&#34;&gt;Scenario 1: Everybody Uses Native VLAN 1&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/rstp-native-vlan-20-1-allowed.webp&#34; alt=&#34;Scenario where native VLAN is the default native VLAN 1 and MikroTik bridgeblocks all traffic from all VLANs to a Ciscoswitch&#34;&gt;&lt;/p&gt;&lt;p&gt;In this scenario:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;VLAN 1 is the native VLAN&lt;/li&gt;&lt;li&gt;VLAN 20 is tagged&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;What happens in VLAN 1:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Cisco sends untagged VLAN 1 BPDU both to IEEE and Cisco addresses&lt;/li&gt;&lt;li&gt;Cisco processes untagged RSTP BPDUs coming from MikroTiks under VLAN 1&lt;/li&gt;&lt;li&gt;STP converges and blocks a port to prevent a switching loop&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;What happens in VLAN 20:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Cisco starts out assuming it is the root bridge on VLAN 20&lt;/li&gt;&lt;li&gt;Cisco sends tagged VLAN 20 BPDUs&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; to the Cisco address&lt;/li&gt;&lt;li&gt;MikroTiks see Cisco address as just another multicast address and flood it&lt;/li&gt;&lt;li&gt;For a brief period of time, before VLAN 1 STP converges and blocks a port,Cisco receives its own BPDU on its other interface and decides that it&amp;rsquo;sconnected to a shared segment&lt;/li&gt;&lt;li&gt;Cisco transitions the port with the lowest port priority to the backup roleand discards incoming traffic on that port&lt;/li&gt;&lt;li&gt;After VLAN 1 converges and a port is blocked, Cisco stops receiving its ownBPDUs on the backup port, decides that a topology change has taken place andtransitions the port&amp;rsquo;s role from backup to designated port&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In the (R)PVST+ world, the Cisco switch will be the root on all VLANs becausethere are no other contenders: the switch will never hear anything from anybody,because the MikroTik at the bottom has blocked its outgoing interface to theCisco switch.&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/rstp-wireshark-vlan-1-ieee-cisco.webp&#34; alt=&#34;Wireshark showing Cisco sending untagged VLAN 1 BPDUs to both the IEEE andCisco addresses&#34;&gt;&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Switch#show spanning-tree vlan 20VLAN0020  Spanning tree enabled protocol rstp  Root ID    Priority    32778             Address     1ce6.c707.ff00             This bridge is the root             Hello Time   2 sec  Max Age 20 sec  Forward Delay 15 sec  Bridge ID  Priority    32788  (priority 32768 sys-id-ext 20)             Address     1ce6.c707.ff00             Hello Time   2 sec  Max Age 20 sec  Forward Delay 15 sec             Aging Time  300 secInterface           Role Sts Cost      Prio.Nbr Type------------------- ---- --- --------- -------- --------------------------------Gi0/1               Desg FWD 4         128.9    P2pGi0/2               Back BLK 19        128.10   P2p&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Interface           Role Sts Cost      Prio.Nbr Type------------------- ---- --- --------- -------- --------------------------------Gi0/1               Desg FWD 4         128.9    P2pGi0/2               Desg FWD 19        128.10   P2p&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Any loops?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;After convergence in VLAN 1, a port is blocked and no loop is possible&lt;/li&gt;&lt;li&gt;This blocked port also prevents loops on RPVST+ VLAN 20, even though the Ciscoput both its ports in forwarding state&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&#34;scenario-2-native-vlan-20-vlan-1-allowed-on-cisco-trunk&#34;&gt;Scenario 2: Native VLAN 20, VLAN 1 Allowed on Cisco Trunk&lt;/h3&gt;&lt;p&gt;Things get more interesting when you change the native VLAN.&lt;/p&gt;&lt;p&gt;In this scenario:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;VLAN 20 is the native untagged VLAN&lt;/li&gt;&lt;li&gt;VLAN 1 is just another VLAN allowed on the trunk&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;With regard to VLAN 1:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Cisco sends untagged VLAN 1 BPDUs to the IEEE address&lt;/li&gt;&lt;li&gt;Cisco sends tagged VLAN 1 BPDUs to the Cisco address&lt;/li&gt;&lt;li&gt;MikroTiks process untagged VLAN 1 BPDUs as RSTP messages&lt;/li&gt;&lt;li&gt;MikroTiks flood tagged VLAN 1 BPDUs just like any other multicast traffic&lt;/li&gt;&lt;li&gt;The network converges and the same port is blocked as in scenario 1&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;With regard to VLAN 20:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Cisco sends untagged VLAN 20 BPDUs to the Cisco address&lt;/li&gt;&lt;li&gt;MikroTiks flood tagged VLAN 1 BPDUs just like any other multicast traffic&lt;/li&gt;&lt;li&gt;Because a port gets blocked by RSTP on the MikroTiks, the Cisco port with thelowest priority first becomes a backup port and soon afterwards a designatedport, just as in scenario 1&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Again, loops are prevented: a flooded &lt;a href=&#34;https://en.wikipedia.org/wiki/Broadcast%2C_unknown-unicast_and_multicast_traffic&#34;&gt;broadcast, unknown unicast or multicast(BUM) frame&lt;/a&gt; arriving on the MikroTiks will not result in a &lt;a href=&#34;https://en.wikipedia.org/wiki/Broadcast_storm&#34;&gt;broadcaststorm&lt;/a&gt;. Also, any BUM traffic arriving on the Cisco switch from any of theother VLANs via access links will also not cause a broadcast storm because theMikroTik blocks all traffic on all VLANs.&lt;/p&gt;&lt;h3 id=&#34;scenario-3-native-vlan-20-vlan-1-disallowed-on-cisco-trunk&#34;&gt;Scenario 3: Native VLAN 20, VLAN 1 Disallowed on Cisco Trunk&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/rstp-native-vlan-20-vlan-1-disallowed.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&lt;p&gt;In this scenario:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;VLAN 20 is the native untagged VLAN&lt;/li&gt;&lt;li&gt;VLAN 1 is not allowed&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;With regard to VLAN 1:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Cisco does not initiate any BPDUs to the IEEE address&lt;/li&gt;&lt;li&gt;Cisco drops received BPDUs addressed to the IEEE address&lt;/li&gt;&lt;li&gt;MikroTiks see that Cisco does not participate in the root-bridge election byinitiating its own BPDUs or forwarding them, so the MikroTiks ports facing theCisco switch automatically become the designated ports&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;With regard to VLAN 20:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Cisco sends untagged VLAN 20 BPDUs to the Cisco address&lt;/li&gt;&lt;li&gt;MikroTiks flood untagged VLAN 20 BPDUs just like any other multicast traffic&lt;/li&gt;&lt;li&gt;Cisco sets a port with the lowest priority to be the backup port and discardstraffic&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;A switching loop is prevented, because all traffic (both tagged and untagged)coming in on the Cisco switch will not be switched out of the (discarding)backup port.&lt;/p&gt;&lt;p&gt;In the image above, note that it merges 2 views. Cisco views the world as theroot bridge of the native VLAN 20 with no other contenders. Because it receivesits own RPVST+ BPDU on its other port, it designates the other port as a backupand blocks traffic over it. The MikroTiks have another view of the native VLAN(might be or not VLAN 20): because the Cisco does not initiate or forward IEEERSTP BPDUs, it considers ports going out to the Cisco to be designated ports andforward traffic over them. So we end up with 2 isolated root bridges. But all iswell and no loops occur.&lt;/p&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;So is it safe to mix MikroTiks and Cisco bridges with regard to switching loops?The answer seems to be yes, at least for the simple 3-bridge topology anddifferent configurations I examined here. As to more complex topologies andconfiguration, we can say a couple of things:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Cisco&amp;rsquo;s proprietary (R)PVST+ BPDUs will always be flooded by MikroTik bridges&lt;/li&gt;&lt;li&gt;Those MikroTik bridges build their own CST, making sure there is no loopbetween them, blocking ports where necessary&lt;/li&gt;&lt;li&gt;If the Cisco bridges do not forward the IEEE RSTP BPDUs (when they do notallow VLAN 1), the MikroTik bridges will forward on those interfaces.&lt;/li&gt;&lt;li&gt;That would be problematic, were it not that the Cisco switches consider thoseports to be connected to a shared segment (a hub, basically), transition themto backup roles and start discarding traffic.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It&amp;rsquo;s another question entirely whether all of this results in the most efficienttraffic flows. If analysis shows it isn&amp;rsquo;t, it would be best to consider the&lt;a href=&#34;https://en.wikipedia.org/wiki/Multiple_Spanning_Tree_Protocol&#34;&gt;IEEE&amp;rsquo;s standardized Multiple STP&lt;/a&gt; on both the MikroTiks and Cisco&amp;rsquo;s. Butthat&amp;rsquo;s a topic for another time.&lt;/p&gt;&lt;!-- vim: se tw=80 cc=73 sw=0 ts=4 et ai spell: --&gt;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&lt;hr&gt;&lt;ol&gt;&lt;li id=&#34;fn:1&#34;&gt;&lt;p&gt;Unless that VLAN ID happens to be the native VLAN ID, of course.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:2&#34;&gt;&lt;p&gt;(R)PVST+ indicates the originating VLAN ID in its BPDUs by using aso-called TLV (type-length-value) extension header.&lt;img src=&#34;https://relentlesscoding.com/img/bpdu-rpvst-tlv.webp&#34; alt=&#34;A RSTP BPDU showing an extension TLV indicating the originating VLANID&#34; title=&#34;Extension TLV indicates the originating VLANID&#34;&gt;&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Open a ZIP File With Any Extension In Vim</title>
       <link>https://relentlesscoding.com/posts/vim-open-any-file-as-zip/</link>
       <pubDate>Sat, 01 Feb 2025 09:05:39 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/vim-open-any-file-as-zip/</guid>
       <description>&lt;p&gt;What to do when you use Vim to open a zip file, but the file extensionis not &lt;code&gt;.zip&lt;/code&gt; or one of the other extensions known to Vim&amp;rsquo;s zip plugin?&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;displaying-zip-file-content-with-vim&#34;&gt;Displaying ZIP File Content With Vim&lt;/h2&gt;&lt;p&gt;Out of the box, Vim is able to open zip files and display its contents.You can even edit zipped files and save them.&lt;/p&gt;&lt;p&gt;Vim recognizes the following file extensions as containing zippedcontent:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-vim&#34; data-lang=&#34;vim&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt;:&lt;span class=&#34;nx&#34;&gt;zipPlugin_ext&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.&lt;span class=&#34;nx&#34;&gt;aar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;apk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;celzip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;crtx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;docm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;docx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;dotm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;dotx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;ear&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;epub&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.&lt;span class=&#34;nx&#34;&gt;gcsx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;glox&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;gqsx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;ja&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;jar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;kmz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;odb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;odc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;odf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;odg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;odi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.&lt;span class=&#34;nx&#34;&gt;odm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;odp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;ods&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;odt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;otc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;otf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;otg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;oth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;oti&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;otp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;ots&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;ott&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.&lt;span class=&#34;nx&#34;&gt;oxt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;potm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;potx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;ppam&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;ppsm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;ppsx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;pptm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;pptx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;sldx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;thmx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.&lt;span class=&#34;nx&#34;&gt;vdw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;war&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;wsz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;xap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;xlam&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;xlsb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;xlsm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;xlsx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;xltm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;xltx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;*.&lt;span class=&#34;nx&#34;&gt;xpi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.&lt;span class=&#34;nx&#34;&gt;zip&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, when we have a zip file that does not end in one of theseextensions, we need to instruct Vim to execute the zip pluginregardless.&lt;/p&gt;&lt;h2 id=&#34;what-youll-see-when-the-file-extension-is-not-supported&#34;&gt;What You&amp;rsquo;ll See When The File Extension Is Not Supported&lt;/h2&gt;&lt;p&gt;Say, we have a zip file called &lt;code&gt;archive&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ file --mime archive archive: application/zip; charset=binary&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Opening it with Vim results in:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;PK^C^D^@^@^@^@^@^LKAZ^@^@^@^@^@^@^@^@^@^@^@^@^E^@^\^@a.txtUT^@^C7Ú&amp;lt;9d&amp;gt;g7Ú&amp;lt;9d&amp;gt;gux^K^@^A^Dè^C^@^@^Dè^C^@^@PK^C^D... snip ...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Not what we want.&lt;/p&gt;&lt;h2 id=&#34;getting-vim-to-execute-the-zip-plugin-regardless-of-extension&#34;&gt;Getting Vim to Execute the ZIP Plugin Regardless of Extension&lt;/h2&gt;&lt;p&gt;Let&amp;rsquo;s replace the buffer with zip plugin&amp;rsquo;s file browser:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-vim&#34; data-lang=&#34;vim&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;enew&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;call&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;zip&lt;/span&gt;#&lt;span class=&#34;nx&#34;&gt;Browse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/path/to/archive&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s break these commands down:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://vimhelp.org/editing.txt.html#%3Aenew%21&#34;&gt;&lt;code&gt;:enew!&lt;/code&gt;&lt;/a&gt; creates a new buffer, hiding the previous buffer. Theexclamation mark discards any changes made to the original buffer.&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://vimhelp.org/userfunc.txt.html#%3Acall&#34;&gt;&lt;code&gt;:call&lt;/code&gt;&lt;/a&gt; calls a function. The &lt;code&gt;zip#Browse()&lt;/code&gt; function takes apath to a zip file and shows the content of the unzipped file.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Now we get the desired result:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-vim&#34; data-lang=&#34;vim&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;&amp;#34; zip.vim version v34&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;&amp;#34; Browsing zipfile archive&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;&amp;#34; Select a file with cursor and press ENTER&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;.&lt;span class=&#34;nx&#34;&gt;txt&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;.&lt;span class=&#34;nx&#34;&gt;txt&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;always-opening-your-file-extension-with-the-zip-plugin&#34;&gt;Always Opening Your File Extension With the ZIP Plugin&lt;/h2&gt;&lt;p&gt;If you always want to open your file extension to be opened with the zipplugin, you can add the extension to this list and put the following inyour .vimrc:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-vim&#34; data-lang=&#34;vim&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt;:&lt;span class=&#34;nx&#34;&gt;zipPlugin_ext&lt;/span&gt; ..&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;,*.mmm&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;..=&lt;/code&gt; is the string concatenation operator. See &lt;a href=&#34;https://vimhelp.org/eval.txt.html#expr-..&#34;&gt;&lt;code&gt;:h expr-..&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;!-- vim: se tw=72 sw=0 ts=4 et ai: --&gt;</description>
     </item>
   
     <item>
       <title>Build Kotlin JMH Benchmarks With Maven</title>
       <link>https://relentlesscoding.com/posts/benchmark-kotlin-with-jmh/</link>
       <pubDate>Fri, 24 Jan 2025 14:52:10 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/benchmark-kotlin-with-jmh/</guid>
       <description>&lt;p&gt;In this post, we will look at how to set up the Maven infrastructure so we canbenchmark Kotlin code with JMH (the Java Microbenchmarking Harness).&lt;/p&gt;&lt;!-- more --&gt;&lt;p&gt;This post does not look in-depth on how to write JMH benchmarks, but rather howto build and run them.&lt;/p&gt;&lt;h2 id=&#34;create-a-sample-jmh-project&#34;&gt;Create a Sample JMH Project&lt;/h2&gt;&lt;p&gt;Helpfully, &lt;a href=&#34;https://github.com/openjdk/jmh/tree/master/jmh-archetypes/jmh-kotlin-benchmark-archetype&#34;&gt;the JMH project has a Maven archetype for Kotlin&lt;/a&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mvn archetype:generate &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -DinteractiveMode&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -DarchetypeGroupId&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;org.openjdk.jmh &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -DarchetypeArtifactId&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;jmh-kotlin-benchmark-archetype &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -DgroupId&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;com.relentlesscoding &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -DartifactId&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;benchmarks &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -Dversion&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This creates a sample project where we can play around with a simple benchmarkwritten in Kotlin.&lt;/p&gt;&lt;h2 id=&#34;things-to-keep-in-mind-when-creating-jmh-benchmarks&#34;&gt;Things to Keep in Mind When Creating JMH Benchmarks&lt;/h2&gt;&lt;p&gt;Benchmarks look like this:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-kotlin&#34; data-lang=&#34;kotlin&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;com.relentlesscoding&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;org.openjdk.jmh.annotations.Benchmark&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;open&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;MyBenchmark&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nd&#34;&gt;@Benchmark&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;fun&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;benchmarkMethod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;See &lt;a href=&#34;#example-benchmark-creating-signatures-with-different-length-rsa-keys&#34;&gt;a more elaborate benchmarkexample&lt;/a&gt;that benchmarks signing efficiency of different-size RSA keys at the end of thispost. Also see &lt;a href=&#34;https://github.com/openjdk/jmh/tree/master/jmh-samples/src/main/java/org/openjdk/jmh/samples&#34;&gt;the examples the JMH team provides&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Notice that the class needs to be declared &lt;code&gt;open&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Benchmark classes should not be final.                                                            [com.sample.RSASigningMessageBenchmark]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is because JMH needs to be able to subclass it to add instrumentation codeand implement the benchmarking machinery.&lt;/p&gt;&lt;p&gt;If you or your IDE objects to opening classes, you could also use the &lt;code&gt;all-open&lt;/code&gt;compiler plugin that is part of the &lt;code&gt;kotlin-maven-plugin&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.jetbrains.kotlin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;kotlin-maven-plugin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- ... snip ... --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;compilerPlugins&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;combine.children=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;append&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;all-open&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/compilerPlugins&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;pluginOptions&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;option&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        all-open:annotation=org.openjdk.jmh.annotations.BenchmarkMode&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/pluginOptions&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.jetbrains.kotlin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;kotlin-maven-allopen&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${kotlin.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;building-kotlin-jmh-benchmarks-with-maven&#34;&gt;Building Kotlin JMH Benchmarks with Maven&lt;/h2&gt;&lt;p&gt;Compiling the Kotlin benchmark requires the following steps:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Compile the Kotlin source to bytecode:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;kotlin-maven-plugin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.jetbrains.kotlin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${kotlin.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;process-sources&lt;span class=&#34;nt&#34;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;generate-sources&lt;span class=&#34;nt&#34;&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;compile&lt;span class=&#34;nt&#34;&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;sourceDirs&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;nt&#34;&gt;&amp;lt;sourceDir&amp;gt;&lt;/span&gt;${project.basedir}/src/main/kotlin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/sourceDir&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/sourceDirs&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Run the JMH bytecode generator, which:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Analyzes the compiled (Kotlin) benchmark classes&lt;/li&gt;&lt;li&gt;Generates additional Java source files for benchmark infrastructure&lt;/li&gt;&lt;li&gt;Creates metadata for benchmark registration and execution&lt;/li&gt;&lt;/ul&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.codehaus.mojo&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;exec-maven-plugin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;generate-benchmark-code&lt;span class=&#34;nt&#34;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;process-sources&lt;span class=&#34;nt&#34;&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;java&lt;span class=&#34;nt&#34;&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;includePluginDependencies&amp;gt;&lt;/span&gt;true&lt;span class=&#34;nt&#34;&gt;&amp;lt;/includePluginDependencies&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;mainClass&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          org.openjdk.jmh.generators.bytecode.JmhBytecodeGenerator&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/mainClass&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;arguments&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- compiled-bytecode-dir --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;argument&amp;gt;&lt;/span&gt;${project.basedir}/target/classes/&lt;span class=&#34;nt&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- output-source-dir --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;argument&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              ${project.basedir}/target/generated-sources/jmh/&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- output-resource-dir --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;argument&amp;gt;&lt;/span&gt;${project.basedir}/target/classes/&lt;span class=&#34;nt&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- generator-type --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;argument&amp;gt;&lt;/span&gt;default&lt;span class=&#34;nt&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/arguments&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.openjdk.jmh&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jmh-generator-bytecode&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${jmh.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Add the generated JMH Java files as a source to the Maven reactor (otherwiseMaven ignores the source):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.codehaus.mojo&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;build-helper-maven-plugin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.8&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;add-source&lt;span class=&#34;nt&#34;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;process-sources&lt;span class=&#34;nt&#34;&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;add-source&lt;span class=&#34;nt&#34;&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;sources&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;source&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            ${project.basedir}/target/generated-sources/jmh&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;/source&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/sources&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Have the &lt;code&gt;maven-compiler-plugin&lt;/code&gt; compile the whole bunch:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-compiler-plugin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.8.0&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;compilerVersion&amp;gt;&lt;/span&gt;${javac.target}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/compilerVersion&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;source&amp;gt;&lt;/span&gt;${javac.target}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/source&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;target&amp;gt;&lt;/span&gt;${javac.target}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/target&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;compilerArgument&amp;gt;&lt;/span&gt;-proc:none&lt;span class=&#34;nt&#34;&gt;&amp;lt;/compilerArgument&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Finally, you can use the &lt;code&gt;maven-assembly-plugin&lt;/code&gt; or the &lt;code&gt;maven-shade-plugin&lt;/code&gt;to create an uberjar (jar with dependencies). I prefer the assembly pluginbecause it is simpler (the JMH archetype provides an example with the&lt;code&gt;maven-shade-plugin&lt;/code&gt;, though, if you are interested):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-assembly-plugin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;make-uberjar&lt;span class=&#34;nt&#34;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;package&lt;span class=&#34;nt&#34;&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;single&lt;span class=&#34;nt&#34;&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;archive&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;manifest&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;lt;mainClass&amp;gt;&lt;/span&gt;org.openjdk.jmh.Main&lt;span class=&#34;nt&#34;&gt;&amp;lt;/mainClass&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;/manifest&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/archive&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;descriptorRefs&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;descriptorRef&amp;gt;&lt;/span&gt;jar-with-dependencies&lt;span class=&#34;nt&#34;&gt;&amp;lt;/descriptorRef&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/descriptorRefs&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Make sure to put &lt;code&gt;org.openjdk.jmh.Main&lt;/code&gt; as the &lt;code&gt;Main-Class&lt;/code&gt; in the jarmanifest, and not your own main class. This will allow you to tweak the JMHparameters. See &lt;code&gt;java -jar benchmarks-jar-with-dependencies -h&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You are now ready to run the jar with &lt;code&gt;java -jar benchmarks-jar-with-dependencies.jar&lt;/code&gt; from the &lt;code&gt;target/&lt;/code&gt; directory.&lt;/p&gt;&lt;h2 id=&#34;using-a-annotation-processor-instead&#34;&gt;Using a Annotation Processor Instead&lt;/h2&gt;&lt;p&gt;This approach above is pretty explicit. Alternatively, we could use the&lt;code&gt;jmh-generator-annprocess&lt;/code&gt; annotation processor with deprecated &lt;a href=&#34;https://kotlinlang.org/docs/kapt.html&#34;&gt;&lt;code&gt;kapt&lt;/code&gt;&lt;/a&gt; orthe &lt;a href=&#34;https://kotlinlang.org/docs/ksp-overview.html&#34;&gt;Kotlin Symbol Processing (KSP) API&lt;/a&gt; (which I have yet to use). Thatworks pretty well for a Kotlin-only project, but is more difficult to getworking with mixed sources. Also, it might complicate debugging when things gowrong. The benefit is that, this way, we could skip 2 steps in the Maven build:the explicit generation of the bytecode in the &lt;code&gt;exec-maven-plugin&lt;/code&gt; by&lt;code&gt;JmhBytecodeGenerator&lt;/code&gt; and adding the sources to the build in the&lt;code&gt;build-helper-maven-plugin&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;The following would enable &lt;code&gt;kapt&lt;/code&gt; execution as part of the&lt;code&gt;kotlin-maven-plugin&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.jetbrains.kotlin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;kotlin-maven-plugin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- ... snipped compilation execution ... --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;kapt&lt;span class=&#34;nt&#34;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;kapt&lt;span class=&#34;nt&#34;&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;sourceDirs&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;sourceDir&amp;gt;&lt;/span&gt;src/main/kotlin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/sourceDir&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/sourceDirs&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;annotationProcessorPaths&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;annotationProcessorPath&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.openjdk.jmh&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jmh-generator-annprocess&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${jmh.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;/annotationProcessorPath&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/annotationProcessorPaths&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.openjdk.jmh&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jmh-generator-annprocess&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${jmh.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;example-benchmark-creating-signatures-with-different-length-rsa-keys&#34;&gt;Example: Benchmark Creating Signatures With Different-Length RSA Keys&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-kotlin&#34; data-lang=&#34;kotlin&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;com.relentlesscoding.benchmarks&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;org.openjdk.jmh.annotations.*&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;org.openjdk.jmh.infra.Blackhole&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;java.util.*&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;java.util.concurrent.TimeUnit&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;java.security.*&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;java.security.spec.PKCS8EncodedKeySpec&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;java.security.interfaces.RSAPrivateCrtKey&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// override with -bm &amp;lt;thrpt | avgt | sample | ss | all&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@BenchmarkMode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;Mode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Throughput&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// override with -wi &amp;lt;int&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Warmup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;iterations&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// override with -i &amp;lt;int&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Measurement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;iterations&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// override with -tu &amp;lt;s | ms | us | ns&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@OutputTimeUnit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;TimeUnit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MILLISECONDS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Fork&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;open&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;RSASigningMessageBenchmark&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nd&#34;&gt;@State&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;Scope&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Benchmark&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;open&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;BenchmarkState&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;py&#34;&gt;aPKCS8Encoded512BitRSAPrivKey&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEA0fKfr7qTLwt+UBk8oTVks1MmO6L6Fm/15xG8jLx/y274swbQUXO0RZtZWMr8+W00FgdegRNMNgxdIMfZFyfFzQIDAQABAkEAnjos/1Ot+Za/674ZY6XJ7xyLhAagVKisuyky4R5vcfEjK4GgpQO8oOTVP09w8SOZdSa5Vb+6coFpvWs5zwHaAQIhAOhDNww0vkua0HMw/8+jEN9ZpY5NDGCD2mIkNGcaKg75AiEA52eTqbTcC4O5yLHDIblvw6YSylVmdATQOi8i9PZL3nUCIQC70icAyuIb94ybqkMjoMUzKKZ1pZ7dqaJ+/LIXshPS2QIgGNCusSBICKQTpEYL2u374ktI8JG/7uklO1gas5JGCJECIQDEAyVyFTisbbFHqQCUdduakO2XD1yZ5a4ACSTdJoxkBg==&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;py&#34;&gt;aPKCS8Encoded768BitRSAPrivKey&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;MIIB5wIBADANBgkqhkiG9w0BAQEFAASCAdEwggHNAgEAAmEAy5XJREg4ki+HZAzgTF7r6wDUy0gi1Njt4svAWfWT3r/vNGcVep3aGEfP5C262257soZMw9wmpbCtFU97FH8nJ3tb5yc6qzvyVnk56M3WMStLtC5dPenExbDLst08co/fAgMBAAECYQCDtXkLiunGcadW7BmkbviUBeqlRRr7twhX5Nehm4Y54tR/g31a4Yq6kKMHjSpJUjTeQ5EmmqxVVErwxjgJYj3FR+aoy8hk39tTW0OytgXGAmYUyRqpQq7/o63H+zvMMPECMQD4K31WQLRfNsyCDsPaHwzL09H6jqtObYXUCDdNeff+9dE8611qGEA94ypOPQpbmrcCMQDSAi2A2E7+9zjTDdkGQTfhTguyuesLMmXZNU6sDeFGdwZpI1P0/pbBw0XfendfLBkCMQCofWZgPBf6GQtqNboVCkW20T5b3adC3SsiVN2vNWMBcEW6FZZbpNFg8y1S5zB0FysCMQCbT+j/JPonLgbkb5VVPt5oziNwpnbh7P/Nx9LLA+jbCCPBldL9mVs9KYF/aT7nL+ECMQDeYptxqot70d4nWsJoZ5QE8RiU6y7ZGjbDh48QQ87HXjj0ezvr/qMg+eKr8U9/mWs=&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fun&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;parseKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pkcs8EncodedRSAPrivateKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RSAPrivateCrtKey&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;py&#34;&gt;decoded&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Base64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getDecoder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;decode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pkcs8EncodedRSAPrivateKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;py&#34;&gt;keySpec&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PKCS8EncodedKeySpec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;decoded&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;py&#34;&gt;keyFactory&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;KeyFactory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getInstance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;RSA&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;keyFactory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;generatePrivate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;keySpec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RSAPrivateCrtKey&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;py&#34;&gt;a512BitRSAPrivateKey&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parseKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;aPKCS8Encoded512BitRSAPrivKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;py&#34;&gt;a768BitRSAPrivateKey&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parseKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;aPKCS8Encoded768BitRSAPrivKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;fun&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sign&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;privateKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PrivateKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ByteArray&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ByteArray&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;py&#34;&gt;signature&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Signature&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getInstance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;SHA256WithRSA&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;signature&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;initSign&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;privateKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;signature&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;update&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Base64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getEncoder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;encode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;signature&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sign&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;py&#34;&gt;message&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;toByteArray&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nd&#34;&gt;@Benchmark&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fun&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;signWith512BitRSAKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BenchmarkState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bh&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Blackhole&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;bh&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;consume&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sign&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a512BitRSAPrivateKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nd&#34;&gt;@Benchmark&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fun&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;signWith768BitRSAKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BenchmarkState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bh&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Blackhole&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;bh&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;consume&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sign&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a768BitRSAPrivateKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;RSA keys were generated with:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:512 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    openssl pkcs8 -topk8 -nocrypt -outform DER &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    base64&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This number of bits is unsafe for production use. I only chose it to get arelatively small key to prevent infinite horizontal scrolling in this blogpost. In production, use a minimal of 3072-bit RSA keys, which &lt;a href=&#34;https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r5.pdf&#34;&gt;NIST says issafe to use through 2030&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Build the example:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mvn clean package&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run the example:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ java -jar target/benchmarks.jar&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# JMH version: 1.37&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# VM version: JDK 21.0.6, OpenJDK 64-Bit Server VM, 21.0.6+7&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# VM invoker: /usr/lib/jvm/java-21-openjdk/bin/java&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# VM options: &amp;lt;none&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Warmup: 1 iterations, 10 s each&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Measurement: 1 iterations, 10 s each&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Timeout: 10 min per iteration&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Threads: 1 thread, will synchronize iterations&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Benchmark mode: Throughput, ops/time&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Benchmark: com.relentlesscoding.RSASigningMessageBenchmark.signWith512BitRSAKey&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Run progress: 0.00% complete, ETA 00:00:40&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Fork: 1 of 1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Warmup Iteration   1: 19.995 ops/ms&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Iteration   1: 20.506 ops/ms&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Result &lt;span class=&#34;s2&#34;&gt;&amp;#34;com.relentlesscoding.RSASigningMessageBenchmark.signWith512BitRSAKey&amp;#34;&lt;/span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  20.506 ops/ms&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# JMH version: 1.37&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# VM version: JDK 21.0.6, OpenJDK 64-Bit Server VM, 21.0.6+7&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# VM invoker: /usr/lib/jvm/java-21-openjdk/bin/java&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# VM options: &amp;lt;none&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Warmup: 1 iterations, 10 s each&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Measurement: 1 iterations, 10 s each&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Timeout: 10 min per iteration&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Threads: 1 thread, will synchronize iterations&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Benchmark mode: Throughput, ops/time&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Benchmark: com.relentlesscoding.RSASigningMessageBenchmark.signWith768BitRSAKey&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Run progress: 50.00% complete, ETA 00:00:20&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Fork: 1 of 1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Warmup Iteration   1: 8.953 ops/ms&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Iteration   1: 9.193 ops/ms&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Result &lt;span class=&#34;s2&#34;&gt;&amp;#34;com.relentlesscoding.RSASigningMessageBenchmark.signWith768BitRSAKey&amp;#34;&lt;/span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  9.193 ops/ms&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Run complete. Total time: 00:00:40&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# ... snip ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Benchmark                                         Mode  Cnt   Score   Error   Units&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;RSASigningMessageBenchmark.signWith512BitRSAKey  thrpt       20.506          ops/ms&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;RSASigningMessageBenchmark.signWith768BitRSAKey  thrpt        9.193          ops/ms&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Based on this result, it looks like signing with a 512-bit RSA key is abouttwice as fast as signing with a 768-bit key on my hardware.&lt;/p&gt;&lt;!-- vim: se sw=0 ts=2 tw=80 ai: --&gt;</description>
     </item>
   
     <item>
       <title>Migrate Nextcloud PostgreSQL 11 to 17 in a Docker Container</title>
       <link>https://relentlesscoding.com/posts/migrate-docker-nextcloud-postgres-11-to-17/</link>
       <pubDate>Sat, 18 Jan 2025 08:52:54 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/migrate-docker-nextcloud-postgres-11-to-17/</guid>
       <description>&lt;p&gt;This post looks at how to migrate a Postgres Docker image to a newer version. Iwill dump all databases contained in a container and migrate them all into a newcontainer. The process outlined works for any Postgres container, but I discussit in the context of my Nextcloud setup.&lt;/p&gt;&lt;!-- more --&gt;&lt;h2 id=&#34;my-setup&#34;&gt;My Setup&lt;/h2&gt;&lt;p&gt;I have Nextcloud running in containers, with the composition detailed in a&lt;code&gt;docker-compose.yml&lt;/code&gt; file:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Nextcloud&lt;/li&gt;&lt;li&gt;PostgreSQL 11&lt;/li&gt;&lt;li&gt;Redis&lt;/li&gt;&lt;li&gt;Nginx (reverse proxy)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Postgres 11 was EOL for over a year, so it was time to migrate. Mostly to getnew security patches. But newer version also incorporate new features, so a sideeffect might be (slightly) better performance.&lt;/p&gt;&lt;h2 id=&#34;why-not-just-start-a-new-container-using-the-old-data-dir&#34;&gt;Why Not Just Start a New Container Using the Old Data Dir?&lt;/h2&gt;&lt;p&gt;To get the obvious out of the way, the naive approach of just running a newerPostgres image over the old data files will not work, because the data files areincompatible:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; docker run --name pg-17 -e &lt;span class=&#34;nv&#34;&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;postgres &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;&amp;gt;&lt;/span&gt;     -v ./pg-11/data:/var/lib/postgresql/data postgres:17-alpine                                                &lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;PostgreSQL Database directory appears to contain a database; Skipping initialization&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;go&#34;&gt;2025-01-18 07:51:34.219 UTC [1] FATAL:  database files are incompatible with server&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;2025-01-18 07:51:34.219 UTC [1] DETAIL:  The data directory was initialized by PostgreSQL version 11, which is not compatible with this version 17.2.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;the-plan&#34;&gt;The Plan&lt;/h2&gt;&lt;p&gt;So we need to do a data dump and import the data into the new container (but see&lt;a href=&#34;#pg_update&#34;&gt;the &lt;code&gt;pg_upgrade&lt;/code&gt; alternative below&lt;/a&gt;). The plan is:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Stop the Nextcloud service and all its dependencies except for the database&lt;/li&gt;&lt;li&gt;Dump all databases into a file&lt;/li&gt;&lt;li&gt;Stop the old Postgres container&lt;/li&gt;&lt;li&gt;Spin up the new Postgres container&lt;/li&gt;&lt;li&gt;Import the dumped databases into the new container&lt;/li&gt;&lt;li&gt;Restart the Nextcloud service with its dependencies&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&#34;1-stop-nextcloud-service&#34;&gt;1. Stop Nextcloud Service&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; docker compose stop nextcloud nextcloud-redis nextcloud-proxy&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;2-dump-all-databases-into-a-file&#34;&gt;2. Dump All Databases Into a File&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; docker &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; pg-11 pg_dumpall -U postgres -c --if-exists &amp;gt; pg-11.dump&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The dump should be done by a database superuser as indicated by &lt;code&gt;-U &amp;lt;user&amp;gt;, --username=&amp;lt;user&amp;gt;&lt;/code&gt;. In my case, this user is &lt;code&gt;postgres&lt;/code&gt;. I know this, because Ihave a file &lt;code&gt;db.env&lt;/code&gt; that defines &lt;code&gt;POSTGRES_USER&lt;/code&gt; and &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;code&gt;-c, --clean&lt;/code&gt; of &lt;code&gt;pg_dumpall&lt;/code&gt; indicates that the output script will first insert&lt;code&gt;DROP&lt;/code&gt; statements to &lt;code&gt;DROP&lt;/code&gt; existing database objects (roles, databases) beforeinserting them. Combine this with &lt;code&gt;--if-exists&lt;/code&gt; to make them &lt;code&gt;DROP &amp;lt;object&amp;gt; IF EXISTS&lt;/code&gt; statements, reducing the number of errors when importing the data. Noneof this should be necessary on a new database, though, but I just want to makevery sure all the required objects are copied as the stakes are high.&lt;/p&gt;&lt;p&gt;Also note that in a shell, redirection is performed before the command isexecuted. In this case, the standard output is redirected to the file&lt;code&gt;pg-11.dump&lt;/code&gt; before &lt;code&gt;docker exec&lt;/code&gt; is executed. So the redirection affects thecurrent shell and the file will be opened on the host, not the container.&lt;/p&gt;&lt;h2 id=&#34;3-stop-the-running-container&#34;&gt;3. Stop the Running Container&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; docker compose stop nextcloud-db&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;4-start-new-container&#34;&gt;4. Start New Container&lt;/h2&gt;&lt;p&gt;We need to make sure that the new data is stored in a new location. If you runthe container as non-root (which you should), create a data directory for thenew container and &lt;code&gt;chown&lt;/code&gt; it to the correct UID and GID used in the containerto run the &lt;code&gt;postgres&lt;/code&gt; executable:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mkdir -p pg-17/data&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; chown -R pguser:pggroup pg-17&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; chmod -R &lt;span class=&#34;m&#34;&gt;0700&lt;/span&gt; pg-17&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you do run the container as root, you only have to specify the host directoryin &lt;code&gt;docker-compose.yml&lt;/code&gt; (see below). The container, running as root, will beable to create the data directory anywhere on your host and then &lt;code&gt;chown&lt;/code&gt; it tothe &lt;code&gt;postgres&lt;/code&gt; user that is running the &lt;code&gt;postgres&lt;/code&gt; command (by default&lt;code&gt;postgres&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;Edit your &lt;code&gt;docker-compose.yml&lt;/code&gt; (or adjust your &lt;code&gt;-v, --volume&lt;/code&gt; flag to the&lt;code&gt;docker run&lt;/code&gt; command). It looked like this before:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;nextcloud-db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres:11-alpine&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;pg-11&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;pguser:pggroup&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./pg-11/data:/var/lib/postgresql/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;POSTGRES_USER=postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;POSTGRES_PASSWORD=AtriumRabidAntihero&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We will change it to this:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;nextcloud-db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres:17-alpine&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;pg-17&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;pguser:pggroup&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./pg-17/data:/var/lib/postgresql/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;POSTGRES_USER=postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;POSTGRES_PASSWORD=AtriumRabidAntihero&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, start the new container:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; docker compose up --force-recreate nextcloud-db&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Unless you removed the old container, add &lt;code&gt;--force-recreate&lt;/code&gt; to pull the newimage from Docker Hub and run it instead of restarting to old container.&lt;/p&gt;&lt;h2 id=&#34;5-import-the-data-into-new-container&#34;&gt;5. Import the Data Into New Container&lt;/h2&gt;&lt;p&gt;Import from inside the container:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; docker &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; -i pg-17 psql -U postgres &amp;lt; pg-11.dump&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Use the user with superuser privileges to do the import. I do not specify adatabase name, so the default database, matching the user name, will be used.That is &lt;code&gt;postgres&lt;/code&gt; in this case. It does not matter, as the output from&lt;code&gt;pg_dumpall&lt;/code&gt; specifies to drop all databases and then to recreate them.&lt;/p&gt;&lt;p&gt;Notice that we can use the redirection in our host shell. When &lt;code&gt;docker exec&lt;/code&gt; isexecuted, it reads from its standard input: the file &lt;code&gt;pg-11.dump&lt;/code&gt;. For thisinput to reach &lt;code&gt;psql&lt;/code&gt;, we need to keep standard input open by specifying &lt;code&gt;-i, --interactive&lt;/code&gt;. If we don&amp;rsquo;t, &lt;code&gt;psql&lt;/code&gt; does not read any of our commands, seesthat its standard input is closed, and quits. Since we are not typing commandsbut they come from a file, we do not need a prompt (pseudo-TTY) assigned (&lt;code&gt;-t, --tty&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;Consider adding &lt;code&gt;--set ON_ERROR_STOP=1&lt;/code&gt; to the command to terminate the importscript immediately when an error occurs. This is convenient because the sourceof the failure is immediately clear: it is the last attempted statement on thescreen.&lt;/p&gt;&lt;p&gt;Note you cannot import the dump from outside the container:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; psql -h 127.0.0.1 -U postgres &amp;lt; pg-11.dump&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will prompt for your password every time the script needs to connect to anew database. The first time it will succeed, but after the &lt;code&gt;ALTER ROLE postgres&lt;/code&gt; in the script is executed, all next authentication attempts willfail, no matter how sure you were you typed it correctly. This is explainedunder &amp;ldquo;Complications&amp;rdquo;.&lt;/p&gt;&lt;p&gt;Do a sanity check right after the migration:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;postgres=# \l+ nextcloud                                                                                       List of databases   Name    |   Owner   | Encoding | Locale Provider |  Collate   |   Ctype    | Locale | ICU Rules |   Access privileges   |  Size   | Tablespace |                Description                 -----------+-----------+----------+-----------------+------------+------------+--------+-----------+-----------------------+---------+------------+-------------------------------------------- nextcloud | oc_stefan | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           |                       | 206 MB  | pg_default | &lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;6-restart-nextcloud&#34;&gt;6. Restart Nextcloud&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; docker compose up&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;complications&#34;&gt;Complications&lt;/h2&gt;&lt;h3 id=&#34;inside-the-container-you-can-connect-to-the-database-without-credentials&#34;&gt;Inside the Container, You Can Connect to the Database Without Credentials&lt;/h3&gt;&lt;p&gt;If you connect to your database from inside the Docker container, either usingthe Unix socket in &lt;code&gt;/var/run/postgresql&lt;/code&gt; or from the localhost IP address&lt;code&gt;127.0.0.1&lt;/code&gt;, you won&amp;rsquo;t have to enter a password. This does not mean yourconfiguration is wrong or the roles are not password-protected. This is becausethe Postgres container by default trusts connections over these sockets, byvirtue of having added the following lines in&lt;code&gt;/var/usr/postgresql/pg_hba.conf&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# TYPE  DATABASE        USER            ADDRESS                 METHOD# &amp;#34;local&amp;#34; is for Unix domain socket connections onlylocal   all             all                                     trust# IPv4 local connections:host    all             all             127.0.0.1/32            trust# IPv6 local connections:host    all             all             ::1/128                 trust# ...snip...host all all all scram-sha-256&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This reads: allow any local user to connect as any PostgreSQL user, includingthe database superuser. Connections from outside the container, however, shouldauthenticate using the &lt;code&gt;scram-sha-256&lt;/code&gt; authentication method.&lt;/p&gt;&lt;h3 id=&#34;connection-to-the-database-denied-even-though-the-credentials-are-correct&#34;&gt;Connection to the Database Denied Even Though The Credentials Are Correct&lt;/h3&gt;&lt;p&gt;After the migration is complete, we can still log in from within the container(because no authentication is required), but connections from outside thecontainer are denied:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; psql -h 127.0.0.1 -U postgres                                      &lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Password for user postgres:                                                                    &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;psql: error: connection to server at &amp;#34;127.0.0.1&amp;#34;, port 5432 failed: FATAL: password authentication failed for user &amp;#34;postgres&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is because Postgres 11 used the &lt;code&gt;md5&lt;/code&gt; authentication method. This methodstores the result of hashing the password with the MD5 algorithm in thedatabase. You can see the hashes in the &lt;code&gt;pg_authid&lt;/code&gt; table:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;postgres=# select rolname, rolpassword from pg_authid where rolname = &amp;#39;postgres&amp;#39;;           rolname           |             rolpassword             -----------------------------+------------------------------------- postgres                    | md53175bce1d3201d16594cebf9d7eb3f9d&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The default authentication mechanism for PostgreSQL 14+ is the (more secure)&lt;code&gt;scram-sha-256&lt;/code&gt;. This authentication mechanism is defined in &lt;code&gt;pg_hba.conf&lt;/code&gt; (seeabove).&lt;/p&gt;&lt;p&gt;What happens is that Postgres uses SCRAM-SHA-256 authentication when youauthenticate, but it cannot compare the output of that function with the hashin the database, because that was hashed with MD5. (The opposite, when &lt;code&gt;md5&lt;/code&gt; isset in &lt;code&gt;pg_hba.conf&lt;/code&gt; but the result of applying SCRAM-SHA-256 is stored in thedatabase does work, though, to make the transition from the old &lt;code&gt;md5&lt;/code&gt; method toSCRAM-based authentication easier.)&lt;/p&gt;&lt;p&gt;To work around this, connect to the database with the superuser from within thecontainer (bypassing authentication), verify that the new PBKDF is in use andset new passwords for all migrated users:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; docker &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; -it pg-17 psql -U postgres&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;postgres=# show password_encryption;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt; password_encryption &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt; ---------------------&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  scram-sha-256&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  (1 row)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;postgres=# \password&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Enter new password for user &amp;#34;postgres&amp;#34;: &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Enter it again: &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is Nextcloud-specific, but the application user for the Nextcloudconnection and its password can be found under&lt;code&gt;/var/www/html/config/config.php&lt;/code&gt;.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;postgres=# \password stefan&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Enter new password for user &amp;#34;stefan&amp;#34;: &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Enter it again: &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Obviously, as an alternative, you could also change the authentication methodin &lt;code&gt;pg_hba.conf&lt;/code&gt; to &lt;code&gt;md5&lt;/code&gt; and be done. This does nothing to improve security,though, and I would not recommend since you are already need to take someaction to get it working.&lt;/p&gt;&lt;p&gt;Make sure you can still connect to the database:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; psql -h 127.0.0.1 -U postgres -c &lt;span class=&#34;s1&#34;&gt;&amp;#39;SELECT version();&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;pg_update&#34;&gt;&lt;code&gt;pg_update&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;I am aware that &lt;a href=&#34;https://www.postgresql.org/docs/current/pgupgrade.html&#34;&gt;&lt;code&gt;pg_upgrade&lt;/code&gt;&lt;/a&gt; exists and can be used with Docker containers.I have not tried it, because it feels a bit more error-prone than dumping thedata and loading it in a new container.&lt;/p&gt;&lt;p&gt;To continue that tangent, &lt;a href=&#34;https://github.com/tianon/docker-postgres-upgrade&#34;&gt;there is a Docker image&lt;/a&gt; we could use to&lt;code&gt;pg_upgrade&lt;/code&gt; without having to install any Postgres libraries and executables onour host machines. I have not tried that as well, because I am not a big fan ofrunning &amp;ldquo;random&amp;rdquo; containers on my hosts.&lt;/p&gt;&lt;!-- vim: se tw=80 sw=0 ts=4 et ai cc=73: --&gt;</description>
     </item>
   
     <item>
       <title>How to Create a Shared Git Repo</title>
       <link>https://relentlesscoding.com/posts/git-shared-repo/</link>
       <pubDate>Wed, 01 Jan 2025 16:25:48 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/git-shared-repo/</guid>
       <description>&lt;p&gt;In this post we will take a look at how to set up a shared Git repository. Thegoal is to have multiple different users be able to push to and pull from thisrepository. Security is provided by using Unix users and groups.&lt;/p&gt;&lt;p&gt;Why would you want a shared repository? The alternative, using the same user forall your repositories is bad security. If you want to grant a user access to oneof your repositories, you grant them access to all. By leveraging Unix users andgroups we can can implement least privilege: each user can only access the reposs/he has been granted access to.&lt;/p&gt;&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;&lt;p&gt;I will assume you have a &amp;ldquo;Git server&amp;rdquo; set up. This boils down to having a serverwith Git installed and users having access to this server over SSH. A simpleDocker container with Git and OpenSSH installed will do.&lt;/p&gt;&lt;h2 id=&#34;create-user-and-groups&#34;&gt;Create User and Groups&lt;/h2&gt;&lt;p&gt;Firstly, create a user that will own all the bare Git repositories:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;root@server# useradd -m git&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Secondly, add the users that need access to a particular repo:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;root@server# useradd -m user1root@server# useradd -m user2root@server# groupadd --users user1,user2 go-notify&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You should name the group after the repository. That way, you can grant usersaccess to a repository by adding them to the group that conveniently has thesame name as the group. A name such as &lt;code&gt;shared&lt;/code&gt; would be bad, unless you writecode for a &lt;code&gt;share&lt;/code&gt; daemon. In this post, I will use a group &lt;code&gt;go-notify&lt;/code&gt; toaccompany the shared &lt;code&gt;go-notify&lt;/code&gt; repository.&lt;/p&gt;&lt;p&gt;Let&amp;rsquo;s check our work:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;root@server# getent passwd user1 user2user1:x:12348:12348::/home/user1:/bin/shuser2:x:12349:12349::/home/user2:/bin/shroot@server# root@server# getent group go-notifygo-notify:x:60000:user1,user2&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;create-the-shared-repository&#34;&gt;Create the Shared Repository&lt;/h2&gt;&lt;p&gt;Let the &lt;code&gt;git&lt;/code&gt; user create the shared repository:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git@server$ git init --bare --shared -b main go-notify.git&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;--shared&lt;/code&gt; flag:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;[specifies] that the Git repository is to be shared amongst several users.This allows users belonging to the same group to push into that repository.When specified, the config variable core.sharedRepository is set so that filesand directories under &lt;code&gt;$GIT_DIR&lt;/code&gt; are created with the requested permissions.When not specified, Git will use permissions reported by &lt;code&gt;umask(2)&lt;/code&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Specifically, this means that the owning group will get read + write permissionson all files and directories in the shared repository. It will also set thesetgid permission on all directories, meaning that&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;files and subdirectories created within to inherit its group ownership, ratherthan the primary group of the file-creating process. Created subdirectoriesalso inherit the setgid bit.&lt;/p&gt;&lt;p&gt;&lt;cite&gt;&amp;ndash; &lt;a href=&#34;https://en.wikipedia.org/wiki/Setuid#When_set_on_a_directory&#34;&gt;Wikipedia&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&#34;interlude-setgid-background&#34;&gt;Interlude: &lt;code&gt;setgid&lt;/code&gt; Background&lt;/h2&gt;&lt;p&gt;Normally, when you create a directory, the effective group of your user will bethe owning group:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mkdir whose-is-it$ ls -ld whose-is-itdrwxr-xr-x 2 stefan stefan 40 25 jan 14:23 whose-is-it&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Things change when the setgid bit is set on a directory:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;user$ mkdir shared-dirroot# groupadd somegrouproot# chgrp somegroup shared-dirroot# chmod g+s shared-dir&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that only root can change the group ownership of a file if the effectiveuser ID is not a member of the target group. The same goes for the setgid bit.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;user$ ls -ld shared-dirdrwxrwsr-x 2 user somegroup 4096 Jan 25 13:25 shared-diruser$ touch shared-dir/test.txtuser$ ls -l shared-dir-rw-r--r-- 1 user somegroup 0 Jan 25 13:33 test.txtuser$ mkdir shared-dir/subdiruser$ ls -ld shared-dir/subdirdrwxr-sr-x 2 user1 somegroup 4096 Jan 25 13:34 test/subdir&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This should make clear that setgid ensures that:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;New files inherit group ownership&lt;/li&gt;&lt;li&gt;Subdirectories maintain setgid automatically&lt;/li&gt;&lt;li&gt;Group ownership does not revert to user&amp;rsquo;s EGID&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;continue-setting-up-shared-git-repository&#34;&gt;Continue Setting Up Shared Git Repository&lt;/h2&gt;&lt;p&gt;Now that we understand how the &lt;code&gt;--shared&lt;/code&gt; flag works on &lt;code&gt;git init&lt;/code&gt;, let&amp;rsquo;s checkownership and permissions on our Git repository:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git@server:~$ ls -ld go-notify.git/drwxrwsr-x 7 git git 4096 Jan  1 15:48 go-notify.git//git@server:~$ ls -l go-notify.git/total 32-rw-rw-r-- 1 git git   21 Jan  1 15:48 HEADdrwxrwsr-x 2 git git 4096 Jan  1 15:48 branches/-rw-rw-r-- 1 git git  126 Jan  1 15:48 config-rw-rw-r-- 1 git git   73 Jan  1 15:48 descriptiondrwxrwsr-x 2 git git 4096 Jan  1 15:48 hooks/drwxrwsr-x 2 git git 4096 Jan  1 15:48 info/drwxrwsr-x 4 git git 4096 Jan  1 15:48 objects/drwxrwsr-x 4 git git 4096 Jan  1 15:48 refs/&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Change group ownership of the shared repo to the shared group:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;root@server# chgrp -R go-notify go-notify.git&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://man.archlinux.org/man/core/man-pages/chown.1p.en#:~:text=Unless%20chown,may%20be%20cleared.&#34;&gt;Unless &lt;code&gt;chown&lt;/code&gt; is invoked by a process with appropriate privileges&lt;/a&gt;,  thesetgid is removed after a &lt;code&gt;chown(1)&lt;/code&gt; or &lt;code&gt;chgrp(1)&lt;/code&gt;. Since we invoked it withroot, we should be fine. If you find the setgid bit removed, you set it again:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;root@server# find go-notify -type d -execdir chmod g+s {} \+&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Remember that only root can do this as the &lt;code&gt;git&lt;/code&gt; user is not member of the&lt;code&gt;go-notify&lt;/code&gt; group.&lt;/p&gt;&lt;p&gt;Check ownership and permissions again:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git@server:~$ ls -ld go-notify.git/drwxrwsr-x 7 git go-notify 4096 Jan  1 15:48 go-notify.git//git@server:~$ ls -l go-notify.git/total 32-rw-rw-r-- 1 git go-notify   21 Jan  1 15:48 HEADdrwxrwsr-x 2 git go-notify 4096 Jan  1 15:48 branches/-rw-rw-r-- 1 git go-notify  126 Jan  1 15:48 config-rw-rw-r-- 1 git go-notify   73 Jan  1 15:48 descriptiondrwxrwsr-x 2 git go-notify 4096 Jan  1 15:48 hooks/drwxrwsr-x 2 git go-notify 4096 Jan  1 15:48 info/drwxrwsr-x 4 git go-notify 4096 Jan  1 15:48 objects/drwxrwsr-x 4 git go-notify 4096 Jan  1 15:48 refs/&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;push-with-remote-user&#34;&gt;Push With Remote User&lt;/h2&gt;&lt;p&gt;Remote user &lt;code&gt;user1&lt;/code&gt; can now create a file and push it:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;user1@pc$ git init -b main repouser1@pc$ cd repouser1@pc$ echo &amp;#39;This is user1&amp;#39; &amp;gt; user1.txtuser1@pc$ git add user1.txtuser1@pc$ git commit -m &amp;#39;chore: initial commit by user1&amp;#39;user1@pc$ git remote add origin ssh://user1@server:/home/git/go-notify.git/&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can push:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;user1@pc$ git push -u origin mainuser1@127.0.0.1&amp;#39;s password:fatal: detected dubious ownership in repository at &amp;#39;/home/git/go-notify.git&amp;#39;To add an exception for this directory, call:        git config --global --add safe.directory /home/git/go-notify.gitfatal: Could not read from remote repository.Please make sure you have the correct access rightsand the repository exists.&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;designate-safe-directories&#34;&gt;Designate Safe Directories&lt;/h2&gt;&lt;p&gt;Oops. It turns out that Git is particular about which repositories (directoriesreally) it will fetch from and push to. &lt;a href=&#34;https://man.archlinux.org/man/extra/git/git-config.1.en&#34;&gt;&lt;code&gt;git-config(1)&lt;/code&gt;&lt;/a&gt; gives the reason:&lt;/p&gt;&lt;blockquote&gt;&lt;dl&gt;&lt;dt&gt;&lt;code&gt;safe.directory&lt;/code&gt;&lt;/dt&gt;&lt;dd&gt;These config entries specify Git-tracked directories that areconsidered safe even if they are owned by someone other than the current user.By default, Git will refuse to even parse a Git config of a repository ownedby someone else, let alone run its hooks, and this config setting allows usersto specify exceptions, e.g. for intentionally shared repositories (see the&lt;code&gt;--shared&lt;/code&gt; option in &lt;code&gt;git-init(1)&lt;/code&gt;).&lt;/dd&gt;&lt;/dl&gt;&lt;/blockquote&gt;&lt;p&gt;Since we &lt;em&gt;are&lt;/em&gt; creating a shared directory, for every user that is allowed tointeract with it, set it as a &lt;code&gt;safe.directory&lt;/code&gt; on the server:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;root@server# su -l user1 -c &amp;#39;git config --global \    --add safe.directory /home/git/go-notify.git&amp;#39;root@server# su -l user2 -c &amp;#39;git config --global \    --add safe.directory /home/git/go-notify.git&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that it will not work if you put a trailing forward slash after&lt;code&gt;go-notify.git&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Alternatively, trust the directory system-wide:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;root@server# git config --system \    --add safe.directory /home/git/go-notify.git&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will write to the system-wide configuration at &lt;code&gt;/etc/gitconfig&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Now we can push:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;user1@pc$ git push -u origin mainEnumerating objects: 3, done.Counting objects: 100% (3/3), done.Writing objects: 100% (3/3), 403 bytes | 403.00 KiB/s, done.Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)To ssh://server:/home/git/go-notify.git * [new branch]      HEAD -&amp;gt; main branch &amp;#39;main&amp;#39; set up to track &amp;#39;origin/main&amp;#39;.&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;another-user-can-clone-and-push-as-well&#34;&gt;Another User Can Clone and Push As Well&lt;/h2&gt;&lt;p&gt;&lt;code&gt;user2&lt;/code&gt; can clone the repo and then push its own commit:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;user2@pc:~$ git clone ssh://user2@server:/home/git/go-notify.gituser2@pc:~$ cd go-notify/user2@pc:~$ cat &amp;gt; user2.txtFile written by user2^Duser2@pc:~$ git add user2.txtuser2@pc:~$ git commit -m &amp;#39;chore: first commit by user2&amp;#39;user2@pc:~$ git push&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;alternatives&#34;&gt;Alternatives&lt;/h2&gt;&lt;p&gt;Using &lt;code&gt;git init --shared&lt;/code&gt; is one way of achieving shared repositories. As thisleverages standard Unix groups, this is pretty simple. Another way is leveragingACLs, &lt;a href=&#34;https://relentlesscoding.com/posts/docker-volume-permissions/&#34;&gt;as discussed in a previous blog post&lt;/a&gt;.This is more complex, but also more flexible.&lt;/p&gt;&lt;!-- vim: se sw=0 ts=2 et ai tw=80 cc=73: --&gt;</description>
     </item>
   
     <item>
       <title>Docker Volume Permissions: Mismatch Between Host UID and Container UID</title>
       <link>https://relentlesscoding.com/posts/docker-volume-permissions/</link>
       <pubDate>Mon, 30 Dec 2024 15:40:50 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/docker-volume-permissions/</guid>
       <description>&lt;p&gt;When you have a Docker container with a user that has a different UIDfrom the one on your host, mounting a volume and accessing the files canresult in permission issues.&lt;/p&gt;&lt;p&gt;Let&amp;rsquo;s say the UID of the Docker user is &lt;code&gt;12345&lt;/code&gt;. The files on the hostare owned by UID &lt;code&gt;1000&lt;/code&gt;. This means the permissions do not hash and thecontainer user cannot access the files.&lt;/p&gt;&lt;p&gt;One solution is to use ACLs to grant the container user access.&lt;/p&gt;&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;&lt;p&gt;&lt;a href=&#34;https://wiki.archlinux.org/title/ACL&#34;&gt;After reading the Arch Linux wiki&lt;/a&gt;, check if you filesystem supportsACLs:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# tune2fs -l /dev/sdXY | grep &amp;#34;Default mount options:&amp;#34;Default mount options:    user_xattr acl&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;new-files-inherit-group&#34;&gt;New Files Inherit Group&lt;/h2&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ find ./somedir -type d -execdir chmod g+s {} \+&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;First, we set the SGID bit on all directories in the volume. Now, newlycreated files inherit the group ownership of the directory. When theuser &lt;code&gt;12345&lt;/code&gt; creates a new file or directory inside the Dockercontainer, the host user &lt;code&gt;1000&lt;/code&gt; will still be able to access thosefiles.&lt;/p&gt;&lt;h2 id=&#34;set-inherited-default-acl-on-directories&#34;&gt;Set Inherited Default ACL on Directories&lt;/h2&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ find ./somedir -type d -execdir setfacl -dm u:12345:rwX {} \+&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we set default ACLs on all directories in the volume. Newlycreated files will inherit this default ACL. If the default ACL has&lt;code&gt;user:12345:rwx&lt;/code&gt; specified and we create a new file with &lt;code&gt;touch&lt;/code&gt;, whichuses a mode of &lt;code&gt;00666&lt;/code&gt;, the resulting file will have an ACL of&lt;code&gt;user:12345:rw-&lt;/code&gt;. This is because no permissions can be present that arenot specified in the &lt;code&gt;mode&lt;/code&gt; parameter of &lt;a href=&#34;https://man.archlinux.org/man/core/man-pages/open.2.en&#34;&gt;the &lt;code&gt;open&lt;/code&gt; system call&lt;/a&gt;.&lt;a href=&#34;https://man.archlinux.org/man/core/acl/acl.5.en&#34;&gt;&lt;code&gt;acl(5)&lt;/code&gt;&lt;/a&gt; explains this in more detail.&lt;/p&gt;&lt;h2 id=&#34;set-acl-on-all-files-and-directories&#34;&gt;Set ACL on All Files and Directories&lt;/h2&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ find ./somedir -execdir setfacl -m u:12345:rwX {} \+&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we set &lt;code&gt;rw&lt;/code&gt; permissions on all files and directories. &lt;a href=&#34;https://man.archlinux.org/man/chmod.1&#34;&gt;The &lt;code&gt;X&lt;/code&gt;is handy&lt;/a&gt;, it sets&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;execute/search only if the file is a directory or already has  executepermission  for  some  user&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;So it will only set search permissions on directories, which need it sothe Docker user can descend into them, or on files that are alreadyexecutable.&lt;/p&gt;&lt;h2 id=&#34;see-also&#34;&gt;See Also&lt;/h2&gt;&lt;p&gt;&lt;a href=&#34;https://man.archlinux.org/man/chmod.1&#34;&gt;&lt;code&gt;chmod(1)&lt;/code&gt;&lt;/a&gt;, &lt;a href=&#34;https://man.archlinux.org/man/core/acl/acl.5.en&#34;&gt;&lt;code&gt;acl(5)&lt;/code&gt;&lt;/a&gt;, &lt;a href=&#34;https://man.archlinux.org/man/getfacl.1&#34;&gt;&lt;code&gt;getfacl(1)&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;https://man.archlinux.org/man/setfacl.1&#34;&gt;&lt;code&gt;setfacl(1)&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Postgres Data Types, Sizes and Alignment</title>
       <link>https://relentlesscoding.com/posts/psql-data-types-sizes-alignment/</link>
       <pubDate>Thu, 08 Aug 2024 08:12:55 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/psql-data-types-sizes-alignment/</guid>
       <description>&lt;p&gt;How much storage does a single row in a Postgres table take?&lt;/p&gt;&lt;h2 id=&#34;page-format&#34;&gt;Page Format&lt;/h2&gt;&lt;p&gt;&lt;a href=&#34;https://www.postgresql.org/docs/current/storage-page-layout.html&#34;&gt;A page in a default Postgres installation is 8 KiB in size&lt;/a&gt;. Each page begins with 24 bytes of &lt;code&gt;PageHeaderData&lt;/code&gt;(metainformation). This metainformation is followed by &lt;code&gt;ItemId&lt;/code&gt;s, eachcontaining the offset (from the start of this page) and the length ofthe row it points to. An &lt;code&gt;ItemId&lt;/code&gt; is 4 bytes. &lt;code&gt;ItemId&lt;/code&gt;s are put afterthe &lt;code&gt;PageHeaderData&lt;/code&gt;, the items (rows) themselves are stored from theend of the page backwards.&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/postgres-page-layout.svg&#34; alt=&#34;Page Layout in Postgres&#34;&gt;&lt;/p&gt;&lt;h2 id=&#34;row-sizes&#34;&gt;Row Sizes&lt;/h2&gt;&lt;p&gt;Each row in a table has a fixed-size, 23-byte header, followed by anoptional &amp;ldquo;null bitmap&amp;rdquo;. When &lt;code&gt;NULL&lt;/code&gt;able types are used in a tabledefinition, the bitmap contains a bit per field: 0 if the content ofthis row&amp;rsquo;s field is not &lt;code&gt;NULL&lt;/code&gt;, 1 if it is. When the definition has 9+fields (so more bits than can be contained in a single byte), it growsto 2 bytes. The user data, however, only starts at a fixed MAXALIGNboundary (8 bytes on a 64-bit machine, 4 on 32-bit).&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;=# select pg_column_size(row(null, null, null, null, null, null, null, null)); pg_column_size----------------             24=# select pg_column_size(row(null, null, null, null, null, null, null, null, null)); pg_column_size----------------             32&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(The &lt;code&gt;row()&lt;/code&gt; constructor &lt;a href=&#34;https://www.postgresql.org/docs/current/sql-expressions.html#SQL-SYNTAX-ROW-CONSTRUCTORS&#34;&gt;creates a row on an anonymous recordtype&lt;/a&gt;.)&lt;/p&gt;&lt;p&gt;If we have 8 &lt;code&gt;NULL&lt;/code&gt;able values, the fields fit into a single-byte nullbitmap. If we have more than 8, more bytes are required. In this case, 2bytes. On a 64-bit machine, user data starts at an 8-byte boundary. Sothe user data starts at 23 + 2 + 7 = 32 bytes.&lt;/p&gt;&lt;h2 id=&#34;data-sizes&#34;&gt;Data Sizes&lt;/h2&gt;&lt;p&gt;There are two types of type lengths: fixed-length and variable-lengthtypes. A &lt;code&gt;smallint&lt;/code&gt; (= &lt;code&gt;int2&lt;/code&gt;), for example, is a fixed-length typetaking 2 bytes of storage. A &lt;code&gt;text&lt;/code&gt; type is a variable-length type.Since rows cannot span pages, when the data of a variable-length typeexceeds 2 KiB, it can be stored out-of-line (in another table). In thatcase, instead of the value itself, a pointer to that data is stored.(See the example later on.)&lt;/p&gt;&lt;h2 id=&#34;data-alignment&#34;&gt;Data Alignment&lt;/h2&gt;&lt;p&gt;Each data type has alignment requirements. &lt;a href=&#34;https://www.postgresql.org/docs/current/catalog-pg-type.html&#34;&gt;You can view theserequirements&lt;/a&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;=# select typname, typlen, typalign from pg_type where typname = &amp;#39;int2&amp;#39;; typname | typlen | typalign ---------+--------+---------- int2    |      2 | s        &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For a &lt;code&gt;smallint&lt;/code&gt;/&lt;code&gt;int2&lt;/code&gt;, we see that &lt;code&gt;typalign&lt;/code&gt; is &lt;code&gt;s&lt;/code&gt;. This indicatesalignment on a &lt;code&gt;short&lt;/code&gt; boundary, which equals 2 bytes on most machines.Consequently, if an &lt;code&gt;int2&lt;/code&gt; field is defined after a field that takes anodd amount of storage, a padding byte is inserted:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;=# select pg_column_size(row(true)); pg_column_size----------------             25=# select pg_column_size(row(true, 2::int2)); pg_column_size----------------             28&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the first case: 23 bytes of fixed-size header + 1 byte padding toalign to MAXALIGN + 1 byte for the boolean.&lt;/p&gt;&lt;p&gt;If we throw an &lt;code&gt;int2&lt;/code&gt; in the mix after this boolean, we see that thetotal size increases by 3: 1 padding byte and 2 bytes to store the&lt;code&gt;int2&lt;/code&gt; value.&lt;/p&gt;&lt;h2 id=&#34;toast&#34;&gt;TOAST&lt;/h2&gt;&lt;p&gt;Types with flexible-storage requirements use the internal &lt;code&gt;varlena&lt;/code&gt;type. Depending on the length of the data they store, they have a 1- or4-byte header. Their actual values may be stored in-line (meaning in therow itself) or out-of-line (meaning in another table). Wherever theactual data may live, it can also be compressed. &lt;a href=&#34;https://www.postgresql.org/docs/current/storage-toast.html&#34;&gt;The technique fordealing with arbitrary-length fields is called TOAST&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Values with a single-byte header are not aligned on any particularboundary:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;=# select pg_column_size(row(true, &amp;#39;foo&amp;#39;::text)); pg_column_size----------------             29&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After the 24-byte row header, we see 1 byte for the boolean, and then 1&lt;/p&gt;&lt;ul&gt;&lt;li&gt;3 = 4 bytes for the &lt;code&gt;text&lt;/code&gt; type.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In the 1-byte header, when the most-significant is set, the remaining 7bits indicate the length of the data in bytes (including this header).It therefore can indicate up to 126 bytes of data (2^7 - 1 header byte).&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;=&amp;gt; select pg_column_size(row(true, repeat(&amp;#39;b&amp;#39;, 127))); pg_column_size----------------            159&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here we see that once the &lt;code&gt;text&lt;/code&gt; type exceeds 126 bytes 2 things happen:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;the type now requires a header of 4 bytes; and&lt;/li&gt;&lt;li&gt;it now needs to align on a integer (4-byte) boundary.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;When we calculate ourselves: 24 bytes fixed size header, 1 byte to storethe boolean, 3 padding bytes to make sure the 4-byte &lt;code&gt;text&lt;/code&gt; headerstarts on a 4-byte boundary, 4 bytes for the &lt;code&gt;text&lt;/code&gt; header and finally127 bytes to store the actual string.&lt;/p&gt;&lt;h2 id=&#34;toast-storage-options&#34;&gt;TOAST Storage Options&lt;/h2&gt;&lt;p&gt;TOAST is only used when the data-to-be-stored exceeds&lt;code&gt;TOAST_TYPLE_THRESHOLD&lt;/code&gt; (2 KiB on default installations).&lt;/p&gt;&lt;p&gt;You can set storage for a TOASTable column with &lt;code&gt;ALTER TABLE ... SET STORAGE&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;There are 4 strategies for TOASTable columns:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;PLAIN&lt;/code&gt; (no compression or out-of-line storage is done)&lt;/li&gt;&lt;li&gt;&lt;code&gt;EXTENDED&lt;/code&gt; (attempt compression, then out-of-line storage)&lt;/li&gt;&lt;li&gt;&lt;code&gt;EXTERNAL&lt;/code&gt; (attempt only out-of-line storage)&lt;/li&gt;&lt;li&gt;&lt;code&gt;MAIN&lt;/code&gt; (attempt only compression)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The default storage strategies for types can be seen in &lt;a href=&#34;https://www.postgresql.org/docs/current/catalog-pg-type.html&#34;&gt;the &lt;code&gt;pg_type&lt;/code&gt;system catalog&lt;/a&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;=# select typname, typlen, typalign, typstorage from pg_type where typname = &amp;#39;int8&amp;#39;; typname | typlen | typalign | typstorage---------+--------+----------+------------ int8    |      8 | d        | p=# select typname, typlen, typalign, typstorage from pg_type where typname = &amp;#39;text&amp;#39;; typname | typlen | typalign | typstorage---------+--------+----------+------------ text    |     -1 | i        | x&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;An &lt;code&gt;int8&lt;/code&gt; takes a fixed 8 bytes and is not TOASTable as indicated byboth the &lt;code&gt;typlen&lt;/code&gt; and the &lt;code&gt;typstorage&lt;/code&gt; (&lt;code&gt;p&lt;/code&gt; stands for &lt;code&gt;PLAIN&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;A &lt;code&gt;text&lt;/code&gt; field is a variable-length type. This is indicated by thenegative &lt;code&gt;typlen&lt;/code&gt; of &lt;code&gt;-1&lt;/code&gt;. The storage strategy is &lt;code&gt;x&lt;/code&gt;, which stands for&lt;code&gt;EXTENDED&lt;/code&gt;. If you have a value that is &amp;gt; 2 KiB, it will either becompressed or moved to a another table for storage:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;=# create table t (t text);CREATE TABLE=# insert into t values (repeat(&amp;#39;a&amp;#39;, 1024));INSERT 0 1=# insert into t values (repeat(&amp;#39;a&amp;#39;, 2048));INSERT 0 1=# select pg_column_size(t) from t t; pg_column_size----------------           1028             35&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A &lt;code&gt;text&lt;/code&gt; value of length 1024 is stored in-line after a 4-byte header. A&lt;code&gt;text&lt;/code&gt; value whose storage (including header) exceeds 2 KiB is stored ina TOAST table. In its place, a pointer to the data is inserted in ourtable. &lt;a href=&#34;https://www.postgresql.org/docs/current/storage-toast.html&#34;&gt;The documentation says the pointer data is 18 bytes long&lt;/a&gt;(storing the toast table OID, chuck OID, logical size, physical size andthe compression method used, if any), but here we see that only 11 (35 -24) bytes were used. That is something to figure out for me :)&lt;/p&gt;&lt;h2 id=&#34;find-total-size-on-disk-of-a-table&#34;&gt;Find Total Size On Disk of a Table&lt;/h2&gt;&lt;p&gt;To find the total size of a table, including any indexes and TOASTtables (where oversized data of variable-length types are stored):&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;=# select pg_total_relation_size(&amp;#39;t&amp;#39;); pg_total_relation_size------------------------                  16384&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The same can be achieved when using the &lt;code&gt;psql&lt;/code&gt; CLI client:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;=# \dt+ t                                   List of relations Schema | Name | Type  | Owner | Persistence | Access method | Size  | Description--------+------+-------+-------+-------------+---------------+-------+------------- public | t    | table | owner | permanent   | heap          | 16 kB |&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are &lt;a href=&#34;https://www.postgresql.org/docs/current/functions-admin.html&#34;&gt;more handy functions to calculate different types of objectsavailable&lt;/a&gt;.&lt;/p&gt;&lt;!-- vim: se cc=73 tw=72 sw=0 st=4 et ai: --&gt;</description>
     </item>
   
     <item>
       <title>Remove All CAs From Linux Trust Store</title>
       <link>https://relentlesscoding.com/posts/remove-all-ca-s-from-linux-trust-store/</link>
       <pubDate>Fri, 12 Jul 2024 19:39:01 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/remove-all-ca-s-from-linux-trust-store/</guid>
       <description>&lt;p&gt;On &lt;a href=&#34;https://www.grc.com/sn/sn-951-notes.pdf&#34;&gt;Security Now! podcast #951&lt;/a&gt;, Steve told us that 7organizations in the PKI are responsible for minting upwards of 99% ofall existing certificates. (&lt;a href=&#34;https://docs.google.com/document/d/1sGzaE9QTs-qorr4BTqKAe0AaGKjt5GagyEevDoavWU0&#34;&gt;Possible original source&lt;/a&gt;.)&lt;/p&gt;&lt;p&gt;His suggestion was that we can remove all CAs except for those 7 andstill be able to browse the web normally. I wanted to try that, so Idisabled almost all CAs in Firefox by hand (&lt;code&gt;about:preferences#privacy&lt;/code&gt;and then &amp;ldquo;View Certificates&amp;hellip;&amp;rdquo;). After allowing some other CAs that Iencountered on web sites and that I thought were trustworthy, it workedbeautifully.&lt;/p&gt;&lt;p&gt;I wanted to do the same for &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;wget&lt;/code&gt; and other programs.&lt;/p&gt;&lt;p&gt;First I read &lt;a href=&#34;https://wiki.archlinux.org/title/Certificate#Add_a_certificate_to_a_trust_store&#34;&gt;the Arch Linux Wiki article on TLS certificates&lt;/a&gt;. Apparently, libraries can make use of the PKCS #11interface exposed by Arch. I searched for it, but could not readily findany programs that use this interface. From experimenting, I found that&lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;wget&lt;/code&gt;, &lt;code&gt;openssl&lt;/code&gt; and &lt;code&gt;docker&lt;/code&gt; all obtain their root store fromfiles in &lt;code&gt;/etc/ca-certificates/&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;My tactic is: disallow all CAs, then go about my day, and enable thosethat I encounter &lt;em&gt;and that seem trustworthy&lt;/em&gt;.&lt;/p&gt;&lt;h2 id=&#34;1-check-if-you-can-securely-connect-to-a-remote-host&#34;&gt;1. Check if you can securely connect to a remote host&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;nos.nl&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; Q &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;&amp;gt;&lt;/span&gt;   openssl s_client -verify_return_error -servername &lt;span class=&#34;nv&#34;&gt;$H&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$H&lt;/span&gt;:443&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Connecting to 23.50.131.144&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;CONNECTED(00000003)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;depth=2 C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;verify return:1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;depth=1 C=US, O=DigiCert Inc, CN=DigiCert TLS RSA SHA256 2020 CA1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;verify return:1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;depth=0 C=BE, L=Antwerpen, O=DPG Media Services NV, CN=*.nu.nl&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;verify return:1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;... snip ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;2-disable-all-cas&#34;&gt;2. Disable All CAs&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; sudo ln /etc/ca-certificates/extracted/cadir/*.pem &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;&amp;gt;&lt;/span&gt;   /etc/ca-certificates/trust-source/blocklist/&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; sudo /usr/bin/update-ca-trust&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;3-verify-that-remote-host-is-now-untrusted&#34;&gt;3. Verify that remote host is now untrusted&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;nu.nl&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; Q &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;&amp;gt;&lt;/span&gt;   openssl s_client -verify_return_error -servername &lt;span class=&#34;nv&#34;&gt;$H&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$H&lt;/span&gt;:443&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Connecting to 23.50.131.156&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;CONNECTED(00000003)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;depth=1 C=US, O=DigiCert Inc, CN=DigiCert TLS RSA SHA256 2020 CA1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;verify error:num=20:unable to get local issuer certificate&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;40575E1A63700000:error:0A000086:SSL&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;routines:tls_post_process_server_certificate:certificate verify failed:ssl/statem/statem_clnt.c:2091:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;---&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Certificate chain&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt; 0 s:C=BE, L=Antwerpen, O=DPG Media Services NV, CN=*.nu.nl&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;   i:C=US, O=DigiCert Inc, CN=DigiCert TLS RSA SHA256 2020 CA1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;   a:PKEY: id-ecPublicKey, 256 (bit); sigalg: RSA-SHA256&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;   v:NotBefore: Jun  2 00:00:00 2024 GMT; NotAfter: Jun  4 23:59:59 2025 GMT&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt; 1 s:C=US, O=DigiCert Inc, CN=DigiCert TLS RSA SHA256 2020 CA1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;   i:C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;   v:NotBefore: Apr 14 00:00:00 2021 GMT; NotAfter: Apr 13 23:59:59 2031 GMT&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;... snip ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;4-allow-ca-if-deemed-trustworthy&#34;&gt;4. Allow CA if deemed trustworthy&lt;/h2&gt;&lt;p&gt;If we look at the output under 3., we see that the issuer of theintermediate certificate (&lt;code&gt;s:C=US, O=DigiCert Inc, CN=DigiCert TLS RSA SHA256 2020 CA1&lt;/code&gt;) is DigiCert. To trust this root again:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; find /etc/ca-certificates/trust-source/blocklist/ &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;&amp;gt;&lt;/span&gt;   -iname &lt;span class=&#34;s1&#34;&gt;&amp;#39;*digicert*&amp;#39;&lt;/span&gt; -print -delete&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;./DigiCert_High_Assurance_EV_Root_CA.pem&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;./DigiCert_Global_Root_G2.pem&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;./DigiCert_TLS_RSA4096_Root_G5.pem&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;./DigiCert_Trusted_Root_G4.pem&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;./DigiCert_Assured_ID_Root_G2.pem&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;./DigiCert_Assured_ID_Root_G3.pem&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;./DigiCert_Global_Root_G3.pem&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;./DigiCert_TLS_ECC_P384_Root_G5.pem&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;./DigiCert_Assured_ID_Root_CA.pem&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;./DigiCert_Global_Root_CA.pem&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; /usr/bin/update-ca-trust&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sometimes, an application caches the blocklisted certs. In that case, you needto restart the application. Notably, the Docker daemon needs to be restartedwhenever a change to the certificate store was made.&lt;/p&gt;&lt;h2 id=&#34;5-check-if-domain-gets-validated-again&#34;&gt;5. Check if domain gets validated again&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;nu.nl&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; Q &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; openssl s_client -verify_return_error -servername &lt;span class=&#34;nv&#34;&gt;$H&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$H&lt;/span&gt;:443&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Connecting to 23.50.131.156&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;CONNECTED(00000003)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;depth=2 C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;verify return:1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;depth=1 C=US, O=DigiCert Inc, CN=DigiCert TLS RSA SHA256 2020 CA1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;verify return:1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;depth=0 C=BE, L=Antwerpen, O=DPG Media Services NV, CN=*.nu.nl&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;verify return:1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;... snip ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Cookies</title>
       <link>https://relentlesscoding.com/posts/cookies/</link>
       <pubDate>Sat, 25 May 2024 13:53:55 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/cookies/</guid>
       <description>&lt;p&gt;Under what circumstances cookies are accepted or rejected by user agentsand send back to web servers can be a bit confusing. Let&amp;rsquo;s experiment abit.&lt;/p&gt;&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;&lt;p&gt;Actually, it is pretty simple:&lt;/p&gt;&lt;p&gt;The &lt;em&gt;origin domain&lt;/em&gt; is the domain you&amp;rsquo;re visiting. An origin domain can&lt;strong&gt;set&lt;/strong&gt; cookies for its own domain and any higher-level domains. A clientwill &lt;strong&gt;send&lt;/strong&gt; cookies for the origin domain and any higher-level domains.Conversely, a higher-level domain can &lt;strong&gt;read nor write&lt;/strong&gt; cookies forsubdomains.&lt;/p&gt;&lt;p&gt;So: &lt;code&gt;example.com&lt;/code&gt; can read and write cookies for &lt;code&gt;example.com&lt;/code&gt; but not&lt;code&gt;sub.example.com&lt;/code&gt;. &lt;code&gt;sub.example.com&lt;/code&gt; can read and write cookies for&lt;code&gt;sub.example.com&lt;/code&gt; and &lt;code&gt;example.com&lt;/code&gt;, but &lt;strong&gt;not&lt;/strong&gt; for its sibling&lt;code&gt;sub2.example.com&lt;/code&gt;. Top-level domains (TLDs) are always off-limit foreveryone.&lt;/p&gt;&lt;h2 id=&#34;test-drive&#34;&gt;Test Drive&lt;/h2&gt;&lt;p&gt;We&amp;rsquo;re going to prove all the cases (and some special ones) to ourselvesusing our favorite user agent: &lt;a href=&#34;https://man.archlinux.org/man/curl.1&#34;&gt;&lt;code&gt;curl(1)&lt;/code&gt;&lt;/a&gt;! Please note that Iwon&amp;rsquo;t be showing all of the command-line output everywhere to preventthis post from becoming too large.&lt;/p&gt;&lt;h3 id=&#34;setup&#34;&gt;Setup&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Add the following lines to &lt;code&gt;/etc/hosts&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;127.0.0.1example.com sub1.example.com sub2.sub1.example.com127.0.0.1other.com sub1.other.com sub2.sub1.other.com&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://relentlesscoding.com/posts/create-tls-cert-with-wildcard/&#34;&gt;Create a self-signed certificate&lt;/a&gt;. Every &lt;code&gt;curl&lt;/code&gt; invocationshould specify &lt;code&gt;--cacert &amp;lt;cert&amp;gt;&lt;/code&gt; so &lt;code&gt;curl&lt;/code&gt; will accept and verify theself-signed certificate.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Run the following Golang program:&lt;/p&gt; &lt;details&gt; &lt;summary&gt;&lt;code&gt;cookie.go&lt;/code&gt;&lt;/summary&gt;&lt;pre&gt;&lt;code&gt; // expirements with setting cookies package main import (     &amp;quot;fmt&amp;quot;     &amp;quot;log&amp;quot;     &amp;quot;net/http&amp;quot; ) var counter = 0 var inc = func() string {     counter++     return fmt.Sprintf(&amp;quot;%d&amp;quot;, counter) } func main() {     http.HandleFunc(&amp;quot;/other-domain&amp;quot;, handleOtherDomain)     http.HandleFunc(&amp;quot;/tld&amp;quot;, handleTLD)     http.HandleFunc(&amp;quot;/subdomain&amp;quot;, handleSubdomain)     http.HandleFunc(&amp;quot;/domainless&amp;quot;, handleNoDomain)     http.HandleFunc(&amp;quot;/host-prefix-secure&amp;quot;, handleHostPrefixSecure)     http.HandleFunc(&amp;quot;/host-prefix-not-secure&amp;quot;, handleHostPrefixNotSecure)     http.HandleFunc(&amp;quot;/secure&amp;quot;, handleSecure)     http.HandleFunc(&amp;quot;/not-secure&amp;quot;, handleNotSecure)     go func() {         log.Fatal(http.ListenAndServeTLS(&amp;quot;127.0.0.1:443&amp;quot;, &amp;quot;cert.pem&amp;quot;, &amp;quot;key.pem&amp;quot;, nil))     }()     go func() {         log.Fatal(http.ListenAndServe(&amp;quot;127.0.0.1:80&amp;quot;, nil))     }()     select {} } // handleOtherDomain tries to set cookie for different domain func handleOtherDomain(w http.ResponseWriter, _ *http.Request) {     http.SetCookie(w, &amp;amp;http.Cookie{         Name:   &amp;quot;other&amp;quot;,         Value:  inc(),         Domain: &amp;quot;other.com&amp;quot;,     }) } // handleTLD tries to set a &amp;quot;supercookie&amp;quot; for a TLD func handleTLD(w http.ResponseWriter, _ *http.Request) {     http.SetCookie(w, &amp;amp;http.Cookie{         Name:   &amp;quot;tld&amp;quot;,         Value:  inc(),         Domain: &amp;quot;com&amp;quot;,     }) } // handleSubdomain sets 3 cookies for parent and 2 subdomains func handleSubdomain(w http.ResponseWriter, _ *http.Request) {     // can be set on parent or any subdomain of parent     http.SetCookie(w, &amp;amp;http.Cookie{         Name:   &amp;quot;parent&amp;quot;,         Value:  inc(),         Domain: &amp;quot;example.com&amp;quot;,     })     // can be set from sub1 domain or any subdomain of sub1     http.SetCookie(w, &amp;amp;http.Cookie{         Name:   &amp;quot;sub1&amp;quot;,         Value:  inc(),         Domain: &amp;quot;sub1.example.com&amp;quot;,     })     // can be set from sub2 domain or any subdomain of sub2     http.SetCookie(w, &amp;amp;http.Cookie{         Name:   &amp;quot;sub2&amp;quot;,         Value:  inc(),         Domain: &amp;quot;sub2.sub1.example.com&amp;quot;,     }) } // handleNoDomain set cookie w/ no Domain attribute: can be set on origin // domain only and will be sent only to origin domain, not subdomains func handleNoDomain(w http.ResponseWriter, r *http.Request) {     http.SetCookie(w, &amp;amp;http.Cookie{         Name:  fmt.Sprintf(&amp;quot;domainless-%s&amp;quot;, r.Host),         Value: inc(),     }) } // handleHostPrefixSecure sets &amp;quot;domain-locked&amp;quot; cookie: has Secure attr, no // Domain and is only accepted when sent over secure connection func handleHostPrefixSecure(w http.ResponseWriter, _ *http.Request) {     http.SetCookie(w, &amp;amp;http.Cookie{         Name:   &amp;quot;__Host-host&amp;quot;,         Value:  inc(),         Secure: true,     }) } // handleHostPrefixNotSecure tries to set __Host- cookie w/o Secure attr // these &amp;quot;domain-locked&amp;quot; cookies should have Secure attr, no Domain and // sent over secure connection func handleHostPrefixNotSecure(w http.ResponseWriter, _ *http.Request) {     http.SetCookie(w, &amp;amp;http.Cookie{         Name:   &amp;quot;__Host-host&amp;quot;,         Value:  inc(),         Secure: false,     }) } // handleSecure tries to set secure cookie // should only be accepted (and send) by clients when sent over secure connection func handleSecure(w http.ResponseWriter, _ *http.Request) {     http.SetCookie(w, &amp;amp;http.Cookie{         Name:   &amp;quot;secure&amp;quot;,         Value:  inc(),         Domain: &amp;quot;example.com&amp;quot;,         Secure: true,     }) } // handleNotSecure sets cookie with name &amp;quot;secure&amp;quot; but w/o Secure attr set func handleNotSecure(w http.ResponseWriter, _ *http.Request) {     http.SetCookie(w, &amp;amp;http.Cookie{         Name:   &amp;quot;secure&amp;quot;,         Value:  inc(),         Domain: &amp;quot;example.com&amp;quot;,         Secure: false,     }) }&lt;/code&gt;&lt;/pre&gt; &lt;/summary&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&#34;can-a-server-set-a-cookie-for-another-domain&#34;&gt;Can a Server Set a Cookie for Another Domain?&lt;/h3&gt;&lt;p&gt;Let&amp;rsquo;s begin with an empty cookie jar:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ &amp;gt; jar&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s get &amp;ldquo;setting a cookie for another domain&amp;rdquo; out of the way first:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -v -b jar -c jar https://example.com/other-domain... snip ...&amp;gt; GET / HTTP/2&amp;gt; Host: example.com&amp;gt; User-Agent: curl/8.5.0&amp;gt; Accept: */*&amp;gt; &amp;lt; HTTP/2 200 * skipped cookie with bad tailmatch domain: other.com&amp;lt; set-cookie: other=other; Domain=other.com&amp;lt; content-length: 0&amp;lt; date: Sat, 25 May 2024 12:15:58 GMT&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;curl&lt;/code&gt; informs us that &lt;code&gt;example.com&lt;/code&gt; can&amp;rsquo;t set a cookie for &lt;code&gt;other.com&lt;/code&gt;.Check.&lt;/p&gt;&lt;h3 id=&#34;can-a-server-set-a-cookie-for-a-top-level-domain&#34;&gt;Can a Server Set a Cookie for a Top-Level Domain?&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -v -b jar -c jar https://example.com/tld... snip ...* cookie &amp;#39;tld&amp;#39; dropped, domain &amp;#39;example.com&amp;#39; must not set cookies for &amp;#39;com&amp;#39;&amp;lt; set-cookie: tld=tld; Domain=com... snip ...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can&amp;rsquo;t set a cookie for &lt;code&gt;.com&lt;/code&gt; or any other TLD. Another check.&lt;/p&gt;&lt;h3 id=&#34;can-a-server-set-a-cookie-for-subdomains&#34;&gt;Can a Server Set a Cookie for Subdomains?&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -v -b jar -c jar https://example.com/subdomains... snip ...* Added cookie parent=&amp;#34;parent&amp;#34; for domain example.com, path /, expire 0&amp;lt; set-cookie: parent=parent; Domain=example.com* skipped cookie with bad tailmatch domain: sub1.example.com&amp;lt; set-cookie: sub1=sub1; Domain=sub1.example.com* skipped cookie with bad tailmatch domain: sub2.sub1.example.com&amp;lt; set-cookie: sub2=sub2; Domain=sub2.sub1.example.com... snip ...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Only the cookie set for itself is accepted. Cookies for subdomains arerejected. Another check.&lt;/p&gt;&lt;p&gt;Let&amp;rsquo;s examine the cookie jar at this point:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ tail -n +5 jar | column -t.example.com  TRUE  /  FALSE  0  parent  parent&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We notice that the cookie with name &lt;code&gt;parent&lt;/code&gt; was accepted and put in thejar. It&amp;rsquo;s a cookie that the client will send to subdomains as we&amp;rsquo;llsee in a bit. This is indicated by the leading dot &lt;code&gt;.example.com&lt;/code&gt; andthe &lt;code&gt;TRUE&lt;/code&gt; in the second column. The jar file format is explained&lt;a href=&#34;https://everything.curl.dev/http/cookies/fileformat.html&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&#34;do-user-agents-send-cookies-for-parents-to-subdomains&#34;&gt;Do User Agents Send Cookies for Parents to Subdomains?&lt;/h3&gt;&lt;p&gt;Let&amp;rsquo;s make a request to &lt;code&gt;sub1.example.com&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -v -b jar -c jar https://sub1.example.com/subdomains... snip ...&amp;gt; GET / HTTP/2&amp;gt; Host: sub1.example.com&amp;gt; User-Agent: curl/8.5.0&amp;gt; Accept: */*&amp;gt; Cookie: parent=parent&amp;gt; &amp;lt; HTTP/2 200 * Replaced cookie parent=&amp;#34;parent&amp;#34; for domain example.com, path /, expire 0&amp;lt; set-cookie: parent=parent; Domain=example.com* Added cookie sub1=&amp;#34;sub1&amp;#34; for domain sub1.example.com, path /, expire 0&amp;lt; set-cookie: sub1=sub1; Domain=sub1.example.com* skipped cookie with bad tailmatch domain: sub2.sub1.example.com&amp;lt; set-cookie: sub2=sub2; Domain=sub2.sub1.example.com&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&lt;li&gt;The client sends a cookie set on the parent domain to the subdomain.&lt;/li&gt;&lt;li&gt;The client accepts a cookie with the parent domain, replacing theprevious cookie, and for the origin domain.&lt;/li&gt;&lt;li&gt;The cookie for a subdomain, &lt;code&gt;sub2.sub1.example.com&lt;/code&gt; is rejected.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;To really drive this point home, let&amp;rsquo;s make a request to&lt;code&gt;sub2.sub1.example.com&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -v -b jar -c jar https://sub2.sub1.example.com/subdomains&amp;gt; GET / HTTP/2&amp;gt; Host: sub2.sub1.example.com&amp;gt; User-Agent: curl/8.5.0&amp;gt; Accept: */*&amp;gt; Cookie: sub1=sub1; parent=parent&amp;gt; &amp;lt; HTTP/2 200 * Replaced cookie parent=&amp;#34;parent&amp;#34; for domain example.com, path /, expire 0&amp;lt; set-cookie: parent=parent; Domain=example.com* Replaced cookie sub1=&amp;#34;sub1&amp;#34; for domain sub1.example.com, path /, expire 0&amp;lt; set-cookie: sub1=sub1; Domain=sub1.example.com* Added cookie sub2=&amp;#34;sub2&amp;#34; for domain sub2.sub1.example.com, path /, expire 0&amp;lt; set-cookie: sub2=sub2; Domain=sub2.sub1.example.com&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now the jar contains:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ tail -n +5 jar | column -t.sub2.sub1.example.com  TRUE  /  FALSE  0  sub2    sub2.sub1.example.com       TRUE  /  FALSE  0  sub1    sub1.example.com            TRUE  /  FALSE  0  parent  parent&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;are-subdomain-cookies-send-to-the-parent-domain&#34;&gt;Are Subdomain Cookies Send to the Parent Domain?&lt;/h3&gt;&lt;p&gt;What cookies are send to &lt;code&gt;example.com&lt;/code&gt;?&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -v -b jar -c jar https://example.com&amp;gt; GET / HTTP/2&amp;gt; Host: example.com&amp;gt; User-Agent: curl/8.5.0&amp;gt; Accept: */*&amp;gt; Cookie: parent=parent... snip ...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Only the ones set for the origin domain, not for any subdomains.&lt;/p&gt;&lt;h2 id=&#34;special-cases&#34;&gt;Special Cases&lt;/h2&gt;&lt;p&gt;A cookie without a &lt;code&gt;Domain&lt;/code&gt; attribute will be accepted for the origindomain only. It will not be sent with requests to subdomains.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ # empty jar$ &amp;gt; jar$ curl -v -b jar -c jar https://example.com/domainless&amp;gt; GET / HTTP/2&amp;gt; Host: example.com&amp;gt; User-Agent: curl/8.5.0&amp;gt; Accept: */*&amp;gt; &amp;lt; HTTP/2 200 * Added cookie domainless-example.com=&amp;#34;domainless-example.com&amp;#34; for domain example.com, path /, expire 0&amp;lt; set-cookie: domainless-example.com=domainless-example.com&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Check the jar:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ tail -n +5 jar | column -texample.com  FALSE  /  FALSE  0  domainless-example.com  domainless-example.com&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice the missing dot in the cookie jar and &lt;a href=&#34;https://everything.curl.dev/http/cookies/fileformat.html&#34;&gt;the &lt;code&gt;FALSE&lt;/code&gt; in the secondcolumn&lt;/a&gt;. This cookie is &lt;em&gt;not&lt;/em&gt; send to subdomains:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -v -b jar -c jar https://sub1.example.com&amp;gt; GET / HTTP/2&amp;gt; Host: sub1.example.com&amp;gt; User-Agent: curl/8.5.0&amp;gt; Accept: */*&amp;gt; &amp;lt; HTTP/2 200 * Added cookie domainless-sub1.example.com=&amp;#34;domainless-sub1.example.com&amp;#34; for domain sub1.example.com, path /, expire 0&amp;lt; set-cookie: domainless-sub1.example.com=domainless-sub1.example.com... snip ...&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;cookie-prefixes&#34;&gt;Cookie Prefixes&lt;/h3&gt;&lt;p&gt;&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#cookie_prefixes&#34;&gt;Mozilla writes that there are certain prefixes that are respected byHTTP user agents&lt;/a&gt;. Setting a cookie that has a name startingwith &lt;code&gt;__Host-&lt;/code&gt; will be accepted only if (1) the &lt;code&gt;Secure&lt;/code&gt; attribute hasbeen set, (2) it was sent from a secure origin, (3) the &lt;code&gt;Domain&lt;/code&gt;attribute is missing and &lt;code&gt;Path&lt;/code&gt; is set to &lt;code&gt;/&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -v -b jar -c jar https://example.com/host-prefix-secure&amp;gt; GET / HTTP/2&amp;gt; Host: example.com&amp;gt; User-Agent: curl/8.5.0&amp;gt; Accept: */*&amp;gt; &amp;lt; HTTP/2 200 * Added cookie __Host-host=&amp;#34;__Host-host&amp;#34; for domain example.com, path /, expire 0&amp;lt; set-cookie: __Host-host=__Host-host; Secure&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s see what happens when it is sent over an insecure channel:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ # empty jar first$ &amp;gt; jar$ curl -v -b jar -c jar http://example.com/host-prefix-secure&amp;gt; GET / HTTP/1.1&amp;gt; Host: example.com&amp;gt; User-Agent: curl/8.5.0&amp;gt; Accept: */*&amp;gt; &amp;lt; HTTP/1.1 200 OK&amp;lt; Set-Cookie: __Host-host=__Host-host; Secure... snip ...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The jar stays empty:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ tail -n +5 jar | column -t$&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sending over secure channel but not setting &lt;code&gt;Secure&lt;/code&gt; attribute:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -v -b jar -c jar https://example.com/host-prefix-not-secure&amp;gt; GET / HTTP/2&amp;gt; Host: example.com&amp;gt; User-Agent: curl/8.5.0&amp;gt; Accept: */*&amp;gt; &amp;lt; HTTP/2 200 &amp;lt; set-cookie: __Host-host=__Host-host... snip ...$ tail -n +5 jar | column -t$&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Another prefix is &lt;code&gt;__Secure-&lt;/code&gt;. Cookies sent with this prefix are onlyaccepted when it&amp;rsquo;s marked &lt;code&gt;Secure&lt;/code&gt; and send over a secure channel. Soit&amp;rsquo;s weaker than &lt;code&gt;__Host-&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;The reason these prefixes are used is that normally a server can&amp;rsquo;t besure that a cookie was set from a secure origin or even what exactorigin domain set the cookie. Remember that subdomains can set cookiesfor parent domains. A subdomain can insecurely set a cookie that theparent domain also sets with &lt;code&gt;Secure&lt;/code&gt; and therefore expects to besecured.&lt;/p&gt;&lt;h2 id=&#34;the-secure-attribute&#34;&gt;The &lt;code&gt;Secure&lt;/code&gt; Attribute&lt;/h2&gt;&lt;p&gt;Clients reject cookies marked &lt;code&gt;Secure&lt;/code&gt; if received over an insecurechannel:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -v -b jar -c jar http://example.com&amp;gt; GET / HTTP/1.1&amp;gt; Host: example.com&amp;gt; User-Agent: curl/8.5.0&amp;gt; Accept: */*&amp;gt; &amp;lt; HTTP/1.1 200 OK&amp;lt; Set-Cookie: secure=secure; Domain=example.com; Secure$ tail -n +5 jar | column -t$&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Over a secure channel, this cookie is accepted. However, if a subdomainoverrides this cookie and leaves out the &lt;code&gt;Secure&lt;/code&gt; attribute:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -v -b jar -c jar https://sub1.example.com/not-secure&amp;gt; GET / HTTP/2&amp;gt; Host: sub1.example.com&amp;gt; User-Agent: curl/8.5.0&amp;gt; Accept: */*&amp;gt; Cookie: secure=secure&amp;gt; &amp;lt; HTTP/2 200 * Replaced cookie secure=&amp;#34;secure&amp;#34; for domain example.com, path /, expire 0&amp;lt; set-cookie: secure=secure; Domain=example.com$ tail -n +5 jar | column -t.example.com  TRUE  /  FALSE  0  secure  secure&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice that &lt;a href=&#34;https://everything.curl.dev/http/cookies/fileformat.html&#34;&gt;the 4th field is &lt;code&gt;FALSE&lt;/code&gt;&lt;/a&gt;, indicating thiscookie is not &lt;code&gt;Secure&lt;/code&gt;. Now, when we make a request to the parentdomain:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -v -b jar -c jar https://example.com&amp;gt; GET / HTTP/2&amp;gt; Host: example.com&amp;gt; User-Agent: curl/8.5.0&amp;gt; Accept: */*&amp;gt; Cookie: secure=secure&amp;gt; &amp;lt; HTTP/2 200 ... snip ...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So that&amp;rsquo;s why &lt;code&gt;__Host-&lt;/code&gt; and &lt;code&gt;__Secure-&lt;/code&gt; prefixes exist.&lt;/p&gt;&lt;h2 id=&#34;source-code&#34;&gt;Source Code&lt;/h2&gt;&lt;p&gt;You can get the source code used in this post from Source Hut:&lt;a href=&#34;https://git.sr.ht/~neftas/cookie&#34;&gt;https://git.sr.ht/~neftas/cookie&lt;/a&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Create Self-Signed TLS Cert With Wildcard</title>
       <link>https://relentlesscoding.com/posts/create-tls-cert-with-wildcard/</link>
       <pubDate>Sat, 25 May 2024 12:17:05 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/create-tls-cert-with-wildcard/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s create a self-signed certificate that contains a wildcard SubjectAlternative Name (SAN).&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ openssl req -x509 &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -subj &lt;span class=&#34;s2&#34;&gt;&amp;#34;/C=NL/L=Utrecht/O=Relentless Coding/CN=*.example.com&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -addext &lt;span class=&#34;s2&#34;&gt;&amp;#34;subjectAltName = DNS:example.com, DNS:*.example.com&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -newkey rsa:2048 &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -noenc &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -keyout key.pem &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -out cert.pem&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;-addext&lt;/code&gt; allows us to add X509 extensions to the certificate from thecommand line. In older versions of &lt;a href=&#34;https://man.archlinux.org/man/openssl.1ssl&#34;&gt;&lt;code&gt;openssl(1)&lt;/code&gt;&lt;/a&gt; you needed to use aconfiguration file (&lt;code&gt;--config openssl.cnf&lt;/code&gt;) to achieve the same thing.(See &lt;a href=&#34;https://man.archlinux.org/man/core/openssl/x509v3_config.5ssl.en&#34;&gt;&lt;code&gt;x509v3_config(5)&lt;/code&gt;&lt;/a&gt; for more information about X509 extensions.)&lt;/p&gt;&lt;p&gt;Note we can add multiple DNS names to the SAN field by separating themwith commas. This is different from the way this information is providedin the configuration file.&lt;/p&gt;&lt;p&gt;Also note that &lt;code&gt;-noenc&lt;/code&gt; replaced the &lt;code&gt;-nodes&lt;/code&gt; flag in &lt;code&gt;openssl&lt;/code&gt; version3. They both indicate that the private key is not to be encrypted with apassphrase.&lt;/p&gt;&lt;p&gt;Copy &lt;code&gt;key.pem&lt;/code&gt; and &lt;code&gt;cert.pem&lt;/code&gt; to where the server reads TLS certs andrestart it.&lt;/p&gt;&lt;p&gt;Add entry to &lt;code&gt;/etc/hosts&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;127.0.0.1example.com sub.example.com&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Use with &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl --cacert cert.pem https://sub.example.com&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice we use &lt;code&gt;--cacert &amp;lt;cert&amp;gt;&lt;/code&gt;. This way, the server can still presenta self-signed certificate, and we still get the benefit from allcertificate checks &lt;code&gt;curl&lt;/code&gt; does, such as domain checking. (The &lt;code&gt;-k, --insecure&lt;/code&gt; flag disables all those checks, so it will accept &lt;em&gt;anycertificate&lt;/em&gt;, including a certificate for &lt;code&gt;other.com&lt;/code&gt; when connecting to&lt;code&gt;example.com&lt;/code&gt;, which is not always what we want.)&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Overwrite File Without Truncating</title>
       <link>https://relentlesscoding.com/posts/overwrite-file-without-truncating/</link>
       <pubDate>Mon, 01 Apr 2024 09:44:23 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/overwrite-file-without-truncating/</guid>
       <description>&lt;p&gt;In Bash, when you open a file for writing, the file gets truncated.However, there is a way to open a file for both reading and writing atthe same time.&lt;/p&gt;&lt;p&gt;Who hasn&amp;rsquo;t made the mistake of creating a process that tries to bothread and write to the same file?&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; wc -c file&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;17 file&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; sed s/foo/FOO/ &amp;lt;file &amp;gt;file&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; wc -c file&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Oops. That&amp;rsquo;s why for &lt;a href=&#34;https://man.archlinux.org/man/sed.1&#34;&gt;&lt;code&gt;sed(1)&lt;/code&gt;&lt;/a&gt; in particular you use the&lt;code&gt;-i[SUFFIX], --in-place[=SUFFIX]&lt;/code&gt; switch, and in general can use&lt;a href=&#34;https://man.archlinux.org/man/sponge.1&#34;&gt;&lt;code&gt;sponge(1)&lt;/code&gt;&lt;/a&gt; from &lt;a href=&#34;https://joeyh.name/code/moreutils/&#34;&gt;&lt;code&gt;moreutils&lt;/code&gt;&lt;/a&gt; (e.g. &lt;code&gt;grep foo &amp;lt;file | sponge &amp;gt;file&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;But you can also open a file for reading &lt;em&gt;and&lt;/em&gt; writing, by using the&lt;code&gt;[n]&amp;lt;&amp;gt;word&lt;/code&gt; syntax. This is described in &lt;a href=&#34;https://man.archlinux.org/man/bash.1&#34;&gt;&lt;code&gt;bash(1)&lt;/code&gt;&lt;/a&gt; under&amp;ldquo;REDIRECTION&amp;rdquo; &amp;gt; &amp;ldquo;Opening File Descriptors for Reading and Writing&amp;rdquo;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The redirection operator&lt;/p&gt;&lt;pre&gt;&lt;code&gt;    [n]&amp;lt;&amp;gt;word&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;causes the file whose name is the expansion of &lt;code&gt;word&lt;/code&gt; to beopened for both reading and writing on file descriptor &lt;code&gt;n&lt;/code&gt;, oron file descriptor &lt;code&gt;0&lt;/code&gt; if &lt;code&gt;n&lt;/code&gt; is not specified. If the file doesnot exist, it is created.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Test drive:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;mktemp -d&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; ls&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; cat &amp;gt; &lt;span class=&#34;nb&#34;&gt;test&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Hello, world!&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;^D&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; cat &amp;gt;&amp;gt; &lt;span class=&#34;nb&#34;&gt;test&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Another line&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;^D&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; cat 1&amp;lt;&amp;gt; &lt;span class=&#34;nb&#34;&gt;test&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;G&amp;#39;day^D&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; cat &lt;span class=&#34;nb&#34;&gt;test&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;G&amp;#39;day, world!&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Another line&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Bash&#39;s exec Replaces Shell</title>
       <link>https://relentlesscoding.com/posts/bash-exec-replaces-shell/</link>
       <pubDate>Sat, 30 Mar 2024 10:44:10 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/bash-exec-replaces-shell/</guid>
       <description>&lt;p&gt;This is a quick look at Bash&amp;rsquo;s &lt;code&gt;exec&lt;/code&gt; builtin with a simple example.&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://man.archlinux.org/man/bash.1&#34;&gt;&lt;code&gt;bash(1)&lt;/code&gt;&lt;/a&gt; explains &lt;code&gt;exec [command]&lt;/code&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;If command is specified, it replaces the shell. No new process iscreated.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This is mainly useful for &lt;a href=&#34;https://mywiki.wooledge.org/WrapperScript&#34;&gt;wrapper scripts&lt;/a&gt;, where wedo not want to have a Bash process lingering around. Take this scriptnamed &lt;code&gt;test&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sleep inf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When run as &lt;code&gt;./test&lt;/code&gt;, the current shell creates a new Bash instance (theinterpreter specified in the first line of the script with the shebang&lt;code&gt;#!/bin/bash&lt;/code&gt;), and then creates a new process for the &lt;code&gt;sleep&lt;/code&gt; command.So, we have 2 processes running: a Bash and a &lt;code&gt;sleep&lt;/code&gt; process:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; ps -o pid,ppid,cmd -T&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    PID    PPID CMD&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;   2443     969 -bash&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;   5348    2443 /bin/bash ./test&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;   5349    5348 sleep inf&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;   5358    2443 ps -o pid,ppid,cmd -T&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When we background &lt;code&gt;./test&lt;/code&gt; with Ctrl-Z and use &lt;a href=&#34;https://man.archlinux.org/man/ps.1&#34;&gt;&lt;code&gt;ps(1)&lt;/code&gt;&lt;/a&gt; with the&lt;code&gt;-T&lt;/code&gt; flag, it prints the processes associated with the current terminalsession. PID 2443 is the current terminal. We see that 2443 spawned&lt;code&gt;/bin/bash ./test&lt;/code&gt; (PID 5348) which in its turn begat &lt;code&gt;sleep inf&lt;/code&gt; (PID5349).&lt;/p&gt;&lt;p&gt;Now we modify the script to use &lt;code&gt;exec&lt;/code&gt; to execute &lt;code&gt;sleep&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; sleep inf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again, the current shell executes a new Bash process. But instead ofit creating a new &lt;code&gt;sleep&lt;/code&gt; process, the sleep process replaces it. Now weonly have single process (&lt;code&gt;sleep&lt;/code&gt;) running:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; ps -o pid,ppid,cmd -T&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    PID    PPID CMD&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;   2443     969 -bash&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;   5399    2443 sleep inf&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;   5407    2443 ps -o pid,ppid,cmd -T&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here, we see that &lt;code&gt;sleep inf&lt;/code&gt; has as its parent the shell that invoked&lt;code&gt;./test&lt;/code&gt;: it has replaced &lt;code&gt;/bin/bash ./test&lt;/code&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Commands in Pipelines Are Executed in Subshells</title>
       <link>https://relentlesscoding.com/posts/bash-commands-in-pipelines-subshells/</link>
       <pubDate>Sat, 30 Mar 2024 10:44:10 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/bash-commands-in-pipelines-subshells/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s have a look at another one of those gotchas that can bite youwhile scripting in Bash: processes that are part of a pipeline all runin subshells.&lt;/p&gt;&lt;p&gt;What would be result of executing the following Bash script?&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;foo&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; bar &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;read&lt;/span&gt; var&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$var&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you said &amp;ldquo;foo&amp;rdquo;, you would be right. For me, this was reallycounter-intuitive. What is going on?&lt;/p&gt;&lt;p&gt;As always, &lt;a href=&#34;https://man.archlinux.org/man/bash.1&#34;&gt;&lt;code&gt;bash(1)&lt;/code&gt;&lt;/a&gt; has the answer. It explains under&amp;ldquo;SHELL GRAMMAR&amp;rdquo; &amp;gt; &amp;ldquo;Pipelines&amp;rdquo;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Each command in a multi-command pipeline, where pipes are created, isexecuted in a subshell, which is a separate process.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;And also, under &amp;ldquo;COMMAND EXECUTION ENVIRONMENT&amp;rdquo;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Changes made to the subshell environment cannot affect the shell&amp;rsquo;sexecution environment.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;That means a pipeline can actually be represented as:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;new, separate process&amp;gt; | &amp;lt;another new, separate process&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s prove this to ourselves. Under &amp;ldquo;PARAMETERS&amp;rdquo; &amp;gt; &amp;ldquo;SpecialParameters&amp;rdquo;, &lt;a href=&#34;https://man.archlinux.org/man/bash.1&#34;&gt;Bash&amp;rsquo;s man page&lt;/a&gt; explains that Bash expands &lt;code&gt;$$&lt;/code&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;[&amp;hellip;] to the process ID of the shell. In a subshell, it expands to theprocess ID of the current shell, not the subshell.&lt;/p&gt;&lt;/blockquote&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$$&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$$&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;2443&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;2443&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;2443&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To get the PID of the subshell, Bash provides &lt;code&gt;BASHPID&lt;/code&gt; (documentedunder &amp;ldquo;PARAMETERS&amp;rdquo; &amp;gt; &amp;ldquo;Shell Variables&amp;rdquo;):&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Expands to the process ID of the current bash process. This differsfrom &lt;code&gt;$$&lt;/code&gt; under certain circumstances, such as subshells that do notrequire bash to be re-initialized.&lt;/p&gt;&lt;/blockquote&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; Parent: &lt;span class=&#34;nv&#34;&gt;$BASHPID&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; Sub 1: &lt;span class=&#34;nv&#34;&gt;$BASHPID&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; Sub 2: &lt;span class=&#34;nv&#34;&gt;$BASHPID&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Parent: 2443&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Sub 2: 10599&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Sub 1: 10598&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here, the parent shell has PID 2443, and the two subshells have 10599and 10598 respectively.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Create Persistent Connections to Processes Using FIFOs</title>
       <link>https://relentlesscoding.com/posts/shell-script-persistent-connection-to-fifos/</link>
       <pubDate>Tue, 26 Mar 2024 09:21:43 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/shell-script-persistent-connection-to-fifos/</guid>
       <description>&lt;p&gt;In this post, we are going to make a Bash script interact with an HTTPserver and have multiple exchanges using the same connection &lt;a href=&#34;https://man.archlinux.org/man/fifo.7&#34;&gt;byleveraging named pipes/FIFOs&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;My first, naive approach was to first create named pipes &lt;code&gt;i&lt;/code&gt; (for&amp;ldquo;input&amp;rdquo;) and &lt;code&gt;o&lt;/code&gt; (for &amp;ldquo;output&amp;rdquo;). I ran &lt;code&gt;openssl s_client&lt;/code&gt; with its stdinreading from &lt;code&gt;i&lt;/code&gt; and its stdout writing to &lt;code&gt;o&lt;/code&gt; and backgrounded it.Then, I used the shell builtin &lt;code&gt;printf&lt;/code&gt; and &lt;code&gt;cat&lt;/code&gt; to write and read from&lt;code&gt;i&lt;/code&gt; and &lt;code&gt;o&lt;/code&gt; respectively:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkfifo i o&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;bol.com&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openssl s_client -servername &lt;span class=&#34;nv&#34;&gt;$h&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$h&lt;/span&gt;:443 &amp;lt;i &amp;gt;o &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;%s\r\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;GET / HTTP/1.1&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Host: bol.com&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; &amp;gt;i&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat &amp;lt;o&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This results in the following (output is truncated for brevity):&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;CONNECTED(00000003)---Certificate chain 0 s:CN=www.bol.com   i:C=US, O=Let&amp;#39;s Encrypt, CN=R3   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256   v:NotBefore: Mar 17 10:19:22 2024 GMT; NotAfter: Jun 15 10:19:21 2024GMT 1 s:C=US, O=Let&amp;#39;s Encrypt, CN=R3   i:C=US, O=Internet Security Research Group, CN=ISRG Root X1   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256   v:NotBefore: Sep  4 00:00:00 2020 GMT; NotAfter: Sep 15 16:00:00 2025GMT---Server certificate-----BEGIN CERTIFICATE-----... snip ...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In Bash, redirections (e.g. reading from or writing to a named pipe) areprocessed before starting a command. Therefore, Bash blocks while tryingto open the named pipes until both the reading and writing ends areconnected. That means that &lt;code&gt;openssl&lt;/code&gt; will not be executed until it hassome other process writing to its input pipe and reading from its outputpipe.&lt;/p&gt;&lt;p&gt;(We can prove this to ourselves by doing a process listing:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;bol.com&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; openssl s_client -servername &lt;span class=&#34;nv&#34;&gt;$h&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$h&lt;/span&gt;:443 &amp;lt;i &amp;gt;o &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; pgrep openssl&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Even when writing to the input pipe, but not reading yet from the outputpipe, the process is not started yet:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;%s\r\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;GET / HTTP/1.1&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Host: bol.com&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; &amp;gt;i&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; pgrep openssl&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So, only when some other process starts reading from its output will&lt;code&gt;openssl&lt;/code&gt; actually start.&lt;/p&gt;&lt;p&gt;Trying to run &lt;a href=&#34;https://man.archlinux.org/man/strace.1&#34;&gt;&lt;code&gt;strace(1)&lt;/code&gt;&lt;/a&gt; on &lt;code&gt;cat &amp;lt;o&lt;/code&gt; was an eye-opener forme:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; strace cat &amp;lt;o&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This actually blocks any &lt;code&gt;strace&lt;/code&gt; output until the writing end iswritten to, showing clearly that &amp;ldquo;redirection is done before any processis started&amp;rdquo;.&lt;/p&gt;&lt;p&gt;By having a new Bash process execute this code, we can bypass this. Say,we have a file &lt;code&gt;test&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat &amp;lt;i&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We execute this with &lt;code&gt;strace -f -yy bash test&lt;/code&gt;. When that blocks at&lt;code&gt;openat&lt;/code&gt; (see code listing below), we execute &lt;code&gt;/bin/echo foo &amp;gt;i&lt;/code&gt; fromanother terminal window:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;... snip ...[pid  4497] openat(AT_FDCWD&amp;lt;/tmp/tmp.EwoMhpZw3f&amp;gt;, &amp;#34;i&amp;#34;, O_RDONLY) = 3&amp;lt;/tmp/tmp.EwoMhpZw3f/i&amp;gt;[pid  4497] dup2(3&amp;lt;/tmp/tmp.EwoMhpZw3f/i&amp;gt;, 0&amp;lt;/dev/pts/8&amp;lt;char 136:8&amp;gt;&amp;gt;) = 0&amp;lt;/tmp/tmp.EwoMhpZw3f/i&amp;gt;[pid  4497] close(3&amp;lt;/tmp/tmp.EwoMhpZw3f/i&amp;gt;) = 0[pid  4497] execve(&amp;#34;/usr/bin/cat&amp;#34;, [&amp;#34;cat&amp;#34;], 0x5c51da8fdb20 /* 52 vars */) = 0... snip ...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We see that the system call &lt;code&gt;execve&lt;/code&gt; on &lt;code&gt;/usr/bin/cat&lt;/code&gt; was only invokedafter the writing end of the pipe was opened.&lt;/p&gt;&lt;p&gt;Likewise, when &lt;code&gt;strace&lt;/code&gt;ing the script containing &lt;code&gt;/bin/echo foo &amp;gt;i&lt;/code&gt;, wesee:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[pid  9261] openat(AT_FDCWD&amp;lt;/tmp/tmp.wVg9PTBQxY&amp;gt;, &amp;#34;i&amp;#34;, O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3&amp;lt;/tmp/tmp.wVg9PTBQxY/i&amp;gt;[pid  9261] dup2(3&amp;lt;/tmp/tmp.wVg9PTBQxY/i&amp;gt;, 1&amp;lt;/dev/pts/6&amp;lt;char 136:6&amp;gt;&amp;gt;) = 1&amp;lt;/tmp/tmp.wVg9PTBQxY/i&amp;gt;[pid  9261] close(3&amp;lt;/tmp/tmp.wVg9PTBQxY/i&amp;gt;) = 0[pid  9261] execve(&amp;#34;/bin/echo&amp;#34;, [&amp;#34;/bin/echo&amp;#34;, &amp;#34;foo&amp;#34;], 0x5b99e9dfeb90 /* 52 vars */) = 0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;/bin/echo&lt;/code&gt; is only &lt;code&gt;execve&lt;/code&gt;d after opening &lt;code&gt;i&lt;/code&gt;.)&lt;/p&gt;&lt;p&gt;In our HTTP case, the writing happens by &lt;code&gt;printf &amp;gt;i&lt;/code&gt;, the reading by&lt;code&gt;cat &amp;lt;o&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;However, the output reader only sees TLS handshake information. Theconnection to the remote server is closed.  Although &lt;code&gt;strace&lt;/code&gt; confirmsthat &lt;code&gt;openssl&lt;/code&gt; did sent the request to the remote server, we did not seeany HTTP response in the output. Why not?  According to&lt;a href=&#34;https://man.archlinux.org/man/pipe.7&#34;&gt;pipe(7)&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;If  all  file descriptors referring to the write end of a pipe havebeen closed, then an attempt to &lt;code&gt;read(2)&lt;/code&gt; from the pipe will seeend-of-file (&lt;code&gt;read(2)&lt;/code&gt; will return &lt;code&gt;0&lt;/code&gt;).&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;That means that &lt;code&gt;openssl&lt;/code&gt; has seen an EOF when it tried to read from&lt;code&gt;i&lt;/code&gt;, and terminated itself immediately(&lt;a href=&#34;https://man.archlinux.org/man/openssl-s_client.1ssl.en&#34;&gt;&lt;code&gt;openssl-s_client(1)&lt;/code&gt;&lt;/a&gt; provides &lt;code&gt;-ign-eof&lt;/code&gt; to prevent that,BTW).&lt;/p&gt;&lt;p&gt;(To empirically show that the last writer closes the writing end of thepipe:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mkfifo io&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; cat &amp;lt; io&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; cat received EOF&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; cat &amp;gt; io &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; cat &amp;gt; io &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;jobs&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[1]   Running                 { cat &amp;lt; io; echo cat received EOF; } &amp;amp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[2]-  Stopped                 cat &amp;gt; io &amp;amp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[3]+  Stopped                 cat &amp;gt; io &amp;amp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;kill&lt;/span&gt; %3&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[3]+  Terminated              cat &amp;gt; io&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;kill&lt;/span&gt; %2&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[2]+  Terminated              cat &amp;gt; io&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;cat received EOF&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When terminating all writers to &lt;code&gt;io&lt;/code&gt;, the reader &lt;code&gt;cat &amp;lt; io&lt;/code&gt; received&lt;code&gt;EOF&lt;/code&gt; and terminated itself.)&lt;/p&gt;&lt;p&gt;So, when &lt;code&gt;cat &amp;lt;o&lt;/code&gt; reads from the &lt;code&gt;o&lt;/code&gt; pipe, the &lt;code&gt;openssl&lt;/code&gt; process starts.It reads input from &lt;code&gt;i&lt;/code&gt; while sending output to &lt;code&gt;o&lt;/code&gt;. It quits once itsees EOF when trying to read again, before it receives the response fromthe remote site.&lt;/p&gt;&lt;p&gt;Now that we know that a reader sees EOF when the last writer quits, wecan open a file descriptor to the pipe. That file descriptor will stayopen until we close the terminal (or explicitly close it with &lt;code&gt;exec 3&amp;gt;&amp;amp;-&lt;/code&gt;):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; 3&amp;gt;i&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This looks good:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;CONNECTED(00000003)---Certificate chain 0 s:CN=www.bol.com   i:C=US, O=Let&amp;#39;s Encrypt, CN=R3   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256   v:NotBefore: Mar 17 10:19:22 2024 GMT; NotAfter: Jun 15 10:19:21 2024 GMT... snip ...Verify return code: 0 (ok)---HTTP/1.1 301 Moved Permanentlydate: Mon, 01 Apr 2024 13:34:24 GMT... snip ...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This works beautifully. We can send requests from another terminal and&lt;code&gt;cat &amp;lt;o&lt;/code&gt; keeps the output pipe open. If we want to interact with theoutput (e.g. &lt;code&gt;grep&lt;/code&gt; it), however, need something else. We would like tojust keep the output pipe open, &lt;code&gt;read&lt;/code&gt; whatever we want from the outputstream, play with it, make the next request, etc. We cannot simplyterminate &lt;code&gt;cat &amp;lt;o&lt;/code&gt;, because it is the last reader from &lt;code&gt;o&lt;/code&gt;. When&lt;code&gt;openssl&lt;/code&gt; writes to &lt;code&gt;o&lt;/code&gt; while there are no readers left, &lt;code&gt;openssl&lt;/code&gt; willterminate itself. That is because when a process tries to write to apipe that&amp;rsquo;s closed, it will &lt;a href=&#34;https://man.archlinux.org/man/pipe.7&#34;&gt;receive the &lt;code&gt;SIGPIPE&lt;/code&gt; signal from thekernel&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;If all file descriptors referring to the read end of a pipe have beenclosed, then a &lt;code&gt;write(2)&lt;/code&gt; will cause a &lt;code&gt;SIGPIPE&lt;/code&gt; signal to be generatedfor the calling process.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;And we &lt;em&gt;did&lt;/em&gt; close the pipe by terminating &lt;code&gt;cat &amp;lt;o&lt;/code&gt;. But &lt;code&gt;openssl&lt;/code&gt; isnot writing anything, it just sits there, right? Right, but a TLSconnection is a two-way street and the remote server might sendsomething, like a session ticket renewal. &lt;code&gt;openssl&lt;/code&gt; will try to writethis to the output pipe and terminate.&lt;/p&gt;&lt;p&gt;Opening another file descriptor that reads from the output should do it:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; 3&amp;gt;i 4&amp;lt;o&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will keep both the input and output streams open its processends. Finally, we&amp;rsquo;re ready to have as many exchanges as we want:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;bol.com&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; openssl s_client -servername &lt;span class=&#34;nv&#34;&gt;$h&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$h&lt;/span&gt;:443 &amp;lt;i &amp;gt;o &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[1] 5414&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; 3&amp;gt;i 4&amp;lt;o&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[2] 5415&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;%s\r\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;GET / HTTP/1.1&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Host: bol.com&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; &amp;gt;i&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; cat &amp;lt;o&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;... snip certificate stuff ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;HTTP/1.1 301 Moved Permanently&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;date: Thu, 28 Mar 2024 09:14:54 GMT&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;... snip ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;^C&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;%s\r\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;GET / HTTP/1.1&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Host: bol.com&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; &amp;gt;i&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; cat &amp;lt;o&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;HTTP/1.1 301 Moved Permanently&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;date: Thu, 28 Mar 2024 09:15:24 GMT&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;... snip ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;^C&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Upgrade Password Hashing Method for Existing Users</title>
       <link>https://relentlesscoding.com/posts/upgrade-linux-password-hashing-method/</link>
       <pubDate>Mon, 11 Mar 2024 09:06:37 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/upgrade-linux-password-hashing-method/</guid>
       <description>&lt;p&gt;When you&amp;rsquo;ve been using the same Linux system for a while, you might wantto upgrade the password hashing methods used. This article explains howto discover the methods supported by your system, set the preferredmethod and then upgrade the password hashes to make use of the newhashing method.&lt;/p&gt;&lt;p&gt;You can see the supported hashing methods by looking at &lt;a href=&#34;https://man.archlinux.org/man/core/libxcrypt/crypt.5.en&#34;&gt;&lt;code&gt;man 5 crypt&lt;/code&gt;&lt;/a&gt;.  (Note that &lt;a href=&#34;https://man.archlinux.org/man/man.1&#34;&gt;&lt;code&gt;man&lt;/code&gt; section &lt;code&gt;5&lt;/code&gt; indicates &amp;ldquo;Fileformats and conversions, e.g. /etc/passwd&amp;rdquo;.&lt;/a&gt;) Currently, onmy system, &lt;code&gt;yescrypt&lt;/code&gt; is favored and recommended for new hashes.&lt;/p&gt;&lt;p&gt;To check your current hashing method on somebody&amp;rsquo;s account:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;# getent shadow johnjohn:$6$JYJjfxe2YGsIGlvz$7bDibTmj/Iq.EelFEBJd24Ip66fVeBLGK/CMUyOl/9YpgIxtDfq2cLzzAc5b7sXhW3nCc2f4lKzMejFrBr3UF/:18133::::::&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(You can read about the format of &lt;code&gt;shadow&lt;/code&gt; by looking at &lt;a href=&#34;https://man.archlinux.org/man/core/shadow/shadow.5.en&#34;&gt;&lt;code&gt;man 5 shadow&lt;/code&gt;&lt;/a&gt;.) Again, looking at &lt;a href=&#34;https://man.archlinux.org/man/core/libxcrypt/crypt.5.en&#34;&gt;&lt;code&gt;man 5 crypt&lt;/code&gt;&lt;/a&gt;, wesee that the &lt;code&gt;$6$&lt;/code&gt; in the second section after the first colon &lt;code&gt;:&lt;/code&gt;indicates that this password was hashed with &lt;code&gt;sha512crypt&lt;/code&gt; which,according to the same documentation is acceptable for new hashes. But weknow we can do better by using a &amp;ldquo;memory-hard&amp;rdquo; hash such as &lt;code&gt;yescrypt&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://man.archlinux.org/man/core/shadow/passwd.1.en&#34;&gt;&lt;code&gt;passwd(1)&lt;/code&gt;&lt;/a&gt; changes user passwords. It will use thehashing method specified by the &lt;a href=&#34;https://man.archlinux.org/man/core/pam/pam_unix.8.en&#34;&gt;&lt;code&gt;pam_unix.so&lt;/code&gt;&lt;/a&gt; module inthe PAM configuration (&lt;code&gt;/etc/pam.d/passwd&lt;/code&gt;) or else look at &lt;a href=&#34;https://man.archlinux.org/man/login.defs.5&#34;&gt;the&lt;code&gt;ENCRYPT_METHOD&lt;/code&gt; variable in &lt;code&gt;/etc/login.defs&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ grep ^password /etc/pam.d/passwdpasswordincludesystem-auth$ grep ^password /etc/pam.d/system-authpassword   required pam_unix.so try_first_pass nullok shadowpassword   optional pam_permit.so&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(Information about &lt;code&gt;pam_unix.so&lt;/code&gt; arguments can be found in &lt;a href=&#34;https://man.archlinux.org/man/core/pam/pam_unix.8.en&#34;&gt;&lt;code&gt;man 8 pam_unix&lt;/code&gt;&lt;/a&gt;.)&lt;/p&gt;&lt;p&gt;Here, we see that nothing explicit is said about which hashing methodshould be used for passwords. So we turn to&lt;a href=&#34;https://man.archlinux.org/man/login.defs.5&#34;&gt;&lt;code&gt;/etc/login.defs&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ grep ^ENCRYPT_METHOD /etc/login.defsENCRYPT_METHOD YESCRYPT&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, when we upgrade our password with &lt;code&gt;passwd&lt;/code&gt;, it will be protected bythe &lt;code&gt;yescrypt&lt;/code&gt; (&lt;code&gt;$y$&lt;/code&gt;) hashing method:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;# getent shadow johnjohn:$y$j9T$8Z3MNrSUYkSPZWcm83Dh$p.Qev5ogCpuF.KfGBjw1QfBBiV7m05PPWxIGuNtbJc9:19793:0:99999:7:::&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can create a &lt;code&gt;yescrypt&lt;/code&gt; password hash yourself by running:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ export PASS=pass SALT=&#39;$y$j9T$8Z3MNrSUYkSPZWcm83Dh$&#39;$ perl -le &#39;print crypt($ENV{PASS}, $ENV{SALT})&#39;$y$j9T$8Z3MNrSUYkSPZWcm83Dh$p.Qev5ogCpuF.KfGBjw1QfBBiV7m05PPWxIGuNtbJc9&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can read more about the format of the input to&lt;a href=&#34;https://man.archlinux.org/man/core/libxcrypt/crypt.3.en&#34;&gt;&lt;code&gt;crypt(3)&lt;/code&gt;&lt;/a&gt; in &lt;a href=&#34;https://man.archlinux.org/man/core/libxcrypt/crypt.5.en&#34;&gt;&lt;code&gt;man 5 crypt&lt;/code&gt;&lt;/a&gt; under &amp;ldquo;FORMAT OFHASHED PASSPHRASES&amp;rdquo;. Suffice to say here that it consists of a &lt;code&gt;prefix&lt;/code&gt;,&lt;code&gt;options&lt;/code&gt;, a &lt;code&gt;salt&lt;/code&gt; and a &lt;code&gt;hash&lt;/code&gt;, separated by &lt;code&gt;$&lt;/code&gt;. The &lt;code&gt;prefix&lt;/code&gt; fieldindicates which has is to be used and changes what the other fieldsmean.&lt;/p&gt;&lt;p&gt;In this current example, I do not know the exact requirements for the&lt;code&gt;salt&lt;/code&gt;, for instance. I experimented and found that &lt;a href=&#34;https://man.archlinux.org/man/openssl-rand.1ssl&#34;&gt;&lt;code&gt;openssl rand -base64 15&lt;/code&gt;&lt;/a&gt; produces a value that&lt;a href=&#34;https://man.archlinux.org/man/core/libxcrypt/crypt.3.en&#34;&gt;&lt;code&gt;crypt(3)&lt;/code&gt;&lt;/a&gt; grokked.&lt;/p&gt;&lt;p&gt;(Incidentally, password verification is made easy, because you can justcompare the result of &lt;a href=&#34;https://man.archlinux.org/man/core/libxcrypt/crypt.3.en&#34;&gt;&lt;code&gt;crypt(3)&lt;/code&gt;&lt;/a&gt; with the password hash asstored.)&lt;/p&gt;&lt;p&gt;The &lt;code&gt;yescrypt&lt;/code&gt; hashing method takes a cost factor. This can be specifiedin &lt;a href=&#34;https://man.archlinux.org/man/login.defs.5&#34;&gt;&lt;code&gt;/etc/login.defs&lt;/code&gt;&lt;/a&gt; under the &lt;code&gt;YESCRYPT_COST_FACTOR&lt;/code&gt;key. Unlike the KDF rounds used with the &lt;code&gt;sha256crypt&lt;/code&gt; and &lt;code&gt;sha512crypt&lt;/code&gt;methods, which increases complexity linearly, increasing the cost factorof &lt;code&gt;yescrypt&lt;/code&gt; will lead to an exponential increase in memory usage andslow the password comparison down significantly. You might want toexperiment on your system and see what the highest setting is you stillfind bearable. Although you might not want to wait 5 seconds each timeyou log in or want to use &lt;code&gt;sudo&lt;/code&gt;, keep in mind that it will takesomebody that cracks these the same amount of resources for each guess.&lt;/p&gt;&lt;p&gt;See &lt;a href=&#34;https://github.com/openwall/yescrypt/blob/main/PARAMETERS&#34;&gt;&lt;code&gt;yescrypt&lt;/code&gt; parameters&lt;/a&gt; (especially the &lt;code&gt;N&lt;/code&gt;parameter which is influenced by the &lt;code&gt;YESCRYPT_COST_FACTOR&lt;/code&gt; key) and &lt;a href=&#34;https://unix.stackexchange.com/questions/690679/what-does-j9t-mean-in-yescrypt-from-etc-shadow&#34;&gt;adescription of them on the Unix &amp;amp; Linux Stack Exchange&lt;/a&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Maven Wrapper Integrity Validation</title>
       <link>https://relentlesscoding.com/posts/maven-wrapper-integrity-validation/</link>
       <pubDate>Fri, 08 Mar 2024 14:46:48 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/maven-wrapper-integrity-validation/</guid>
       <description>&lt;p&gt;Maven is modular. It uses plugins to achieve its goals. These pluginsare downloaded when they are invoked.&lt;/p&gt;&lt;p&gt;For example, executing &lt;code&gt;mvn wrapper:wrapper&lt;/code&gt; will make Maven first lookin your local repository (&lt;code&gt;~/.m2/repository&lt;/code&gt;), and, if it cannot findit, download the &lt;code&gt;wrapper&lt;/code&gt; plugin from the repository defined in your&lt;code&gt;settings.xml&lt;/code&gt;. (If no custom repository is defined, the default Maven&amp;ldquo;central&amp;rdquo; repository will be used.)&lt;/p&gt;&lt;p&gt;How does &lt;code&gt;mvn&lt;/code&gt; know what to download, though, based only on a prefix(&lt;code&gt;wrapper:&lt;/code&gt;)?  Because the &lt;code&gt;wrapper&lt;/code&gt; plugin used the &lt;a href=&#34;https://maven.apache.org/guides/introduction/introduction-to-plugin-prefix-mapping.html&#34;&gt;Maven prefixrules&lt;/a&gt;. Maven looked in the current project, and not finding what itwas looking for downloaded&lt;a href=&#34;https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-metadata.xml&#34;&gt;https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-metadata.xml&lt;/a&gt;.Now it found this:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;!-- ... snip... --&amp;gt;&amp;lt;plugin&amp;gt;  &amp;lt;name&amp;gt;Apache Maven Wrapper Plugin&amp;lt;/name&amp;gt;  &amp;lt;prefix&amp;gt;wrapper&amp;lt;/prefix&amp;gt;  &amp;lt;artifactId&amp;gt;maven-wrapper-plugin&amp;lt;/artifactId&amp;gt;&amp;lt;/plugin&amp;gt;&amp;lt;!-- ... snip... --&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Maven now knows it can download this plugin from&lt;code&gt;$REPO/org/apache/maven/plugins/maven-wrapper-plugin/$LATEST_VERSION/maven-wrapper-plugin-$LATEST_VERSION.jar&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Recently, the wrapper plugin added SHA-256 checksums to verify theintegrity of &lt;code&gt;maven-wrapper.jar&lt;/code&gt; and the Maven distribution.&lt;/p&gt;&lt;p&gt;These SHA-256 sums are not easily discovered, however. I could not finda web source that listed them. Fortunately, &lt;a href=&#34;https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/&#34;&gt;signatures are uploadedtogether with the source code&lt;/a&gt;. &lt;a href=&#34;https://downloads.apache.org/maven/KEYS&#34;&gt;The public signing keys can be foundelsewhere&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;So, we first download all the Apache Maven certificates (&amp;ldquo;keys&amp;rdquo;) andsign them locally to make them valid:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ curl -o apache-keys.txt https://downloads.apache.org/maven/KEYS$ gpg --import &amp;lt; apache-keys.txt$ while IFS=: read -r type validity key_len pubkey_algo key_id rest; do&amp;gt;   if [[ $type == pub ]] &amp;amp;&amp;amp; ! [[ $validity =~ ^(e|r)$ ]]; then&amp;gt;       gpg --quick-lsign-key &amp;quot;$key_id&amp;quot;&amp;gt;   fi&amp;gt; done &amp;lt; &amp;lt;(gpg --show-keys --with-colons apache-keys.txt 2&amp;gt;/dev/null)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(See for an explanation of the meaning of GnuPG&amp;rsquo;s colon-delimited fields&lt;a href=&#34;https://github.com/gpg/gnupg/blob/master/doc/DETAILS&#34;&gt;https://github.com/gpg/gnupg/blob/master/doc/DETAILS&lt;/a&gt;.)&lt;/p&gt;&lt;p&gt;We&amp;rsquo;ve now locally certified all the imported Apache Maven certificates.(This basically means you&amp;rsquo;ve attested that the UIDs in them belong tothe provided public keys. &amp;ldquo;Locally&amp;rdquo; just means they are not going to bepart of any export of the certificates, for example when uploading to akey server.) GPG has set the validity of these certificates to &amp;ldquo;full&amp;rdquo;.We now accept signatures on files and other data from thesecertificates.&lt;/p&gt;&lt;p&gt;We download the file manually and verify it using the detached signature(file ending in &lt;code&gt;.asc&lt;/code&gt; for &amp;ldquo;ASCII-armored binary&amp;rdquo;):&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ gpg --verify maven-wrapper-3.2.0.jar.asc maven-wrapper-3.2.0.jargpg: Signature made Thu Mar  9 00:07:07 2023 CETgpg:                using RSA key 84789D24DF77A32433CE1F079EB80E92EB2135B1gpg:                issuer &amp;quot;sjaranowski@apache.org&amp;quot;gpg: Good signature from &amp;quot;Slawomir Jaranowski &amp;lt;sjaranowski@apache.org&amp;gt;&amp;quot; [full]gpg:                 aka &amp;quot;Slawomir Jaranowski &amp;lt;s.jaranowski@gmail.com&amp;gt;&amp;quot; [full]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now that we have some confidence that we downloaded a legitimate copy ofthe &lt;code&gt;maven-wrapper.jar&lt;/code&gt; file, we can calculate its SHA-256 sum.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;distributionSha256Sum&lt;/code&gt; should be the SHA-256 checksum of thedownloaded distribution, i.e. the actual Maven tool. According to thedocumentation, we can find the archives here:&lt;a href=&#34;https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip&#34;&gt;https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip&lt;/a&gt;.Again, we download the file manually, let GPG verify it and calculatethe SHA-256 digest.&lt;/p&gt;&lt;p&gt;Now we&amp;rsquo;re ready to kick off the Maven &lt;code&gt;wrapper&lt;/code&gt; plugin:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ mvn wrapper:wrapper \&amp;gt;   -DwrapperSha256Sum=e63a53cfb9c4d291ebe3c2b0edacb7622bbc480326beaa5a0456e412f52f066a \&amp;gt;   -DdistributionSha256Sum=f00af914c785c9faed661f223000a92d1de9553f5c82d3b4362e66d9c031625f&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This downloads the &lt;code&gt;wrapper&lt;/code&gt; plugin which in its turn downloads thewrapper distribution (&lt;code&gt;mvnw&lt;/code&gt; executable and &lt;code&gt;.mvn&lt;/code&gt; directory containingthe wrapper configuration). The configuration file now looks like:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sed &#39;/^#/d&#39; .mvn/wrapper/maven-wrapper.propertiesdistributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zipdistributionSha256Sum=f00af914c785c9faed661f223000a92d1de9553f5c82d3b4362e66d9c031625fwrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jarwrapperSha256Sum=f00a53cfb9c4d291ebe3c2b0edacb7622bbc480326beaa5a0456e412f52f066a&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What is protected by the checksums is not the execution of the &lt;code&gt;mvnw&lt;/code&gt;shell script, but the execution of what that script executes:&lt;code&gt;maven-wrapper.jar&lt;/code&gt;.  When executed, this &lt;code&gt;maven-wrapper.jar&lt;/code&gt; downloadsthe actual Maven distribution (&lt;code&gt;mvn&lt;/code&gt;) if necessary and verifies thedownloaded file against the &lt;code&gt;distributionSha256Sum&lt;/code&gt; in the config file.Now, if the wrapper SHA-256 sum is wrong, the shell script will throw anerror. And if the just-downloaded distribution&amp;rsquo;s SHA-256 sum is wrong,the &lt;code&gt;maven-wrapper.jar&lt;/code&gt; will throw an error.&lt;/p&gt;&lt;p&gt;So, updating the Maven wrapper comes down to:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Download the &lt;code&gt;maven-wrapper.jar&lt;/code&gt; and &lt;code&gt;apache-maven-3.9.6-bin.zip&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Download and verify their detached signatures&lt;/li&gt;&lt;li&gt;Calculate their SHA-256 digests&lt;/li&gt;&lt;li&gt;Execute &lt;code&gt;mvn wrapper:wrapper -DwrapperSha256Sum=&amp;lt;digest&amp;gt; -DdistributionSha256Sum=&amp;lt;digest&amp;gt;&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;More information on the Maven wrapper plugin can be found here:&lt;a href=&#34;https://maven.apache.org/wrapper/&#34;&gt;https://maven.apache.org/wrapper/&lt;/a&gt;.&lt;/p&gt;&lt;!-- vim: set tw=72: --&gt;</description>
     </item>
   
     <item>
       <title>Prevent Exit When Receiving SIGPIPE with Pipefail Set</title>
       <link>https://relentlesscoding.com/posts/prevent-exit-when-sigpipe-received-and-pipefail-set/</link>
       <pubDate>Thu, 29 Feb 2024 09:50:28 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/prevent-exit-when-sigpipe-received-and-pipefail-set/</guid>
       <description>&lt;p&gt;When a program that is part of a pipeline quits, it closes its stdin andstdout. Programs that produce the input for the quitted process may still tryto write to its stdin. Since they cannot, they will receive a SIGPIPE from thekernel.&lt;/p&gt;&lt;p&gt;You can see this in action:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; seq &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;10000&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head -1&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;PIPESTATUS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[@]&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;141 0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;Conventionally, an exit status &lt;code&gt;N&lt;/code&gt; greater than &lt;code&gt;128&lt;/code&gt; indicates the programwas terminated by signal &lt;code&gt;N - 128&lt;/code&gt;. Since &lt;code&gt;SIGPIPE&lt;/code&gt; is signal &lt;code&gt;13&lt;/code&gt;, &lt;code&gt;141 - 128 = 13&lt;/code&gt; indicates your program was ended by a SIGPIPE.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&#34;https://stackoverflow.com/questions/19120263/why-exit-code-141-with-grep-q#comment28274732_19120674&#34;&gt;Source&lt;/a&gt;&lt;/p&gt;&lt;p&gt;When running scripts with &lt;code&gt;set -o pipefail&lt;/code&gt;, this will terminate thescript, however. A workaround is to check for exit code &lt;code&gt;141&lt;/code&gt; and exitthe pipeline with &lt;code&gt;0&lt;/code&gt; instead:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;seq &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;10000&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head -1 &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;kill&lt;/span&gt; -l &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$?&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; PIPE &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; 0&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$?&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Create MacOS App Bundle from Script</title>
       <link>https://relentlesscoding.com/posts/create-macos-app-bundle-from-script/</link>
       <pubDate>Sun, 25 Feb 2024 09:23:10 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/create-macos-app-bundle-from-script/</guid>
       <description>&lt;p&gt;I&amp;rsquo;m going to show how to create the smallest possible MacOS app bundle we can:one that simply executes a Bash script. Then, I&amp;rsquo;m going to expand a bit on thatand add an icon to it. I&amp;rsquo;ll also show how open a Terminal app window so we cansee the script&amp;rsquo;s output in it.&lt;/p&gt;&lt;p&gt;A MacOS app bundle is a directory that is interpreted in a special way byMacOS. It is an application name followed by the suffix &lt;code&gt;.app&lt;/code&gt; (e.g.&lt;code&gt;foo.app&lt;/code&gt;). A minimal app bundle would look like:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mkdir foo.app&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; cat &amp;gt; foo.app/foo&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;#&lt;/span&gt;!/bin/bash -i&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;zenity --calendar&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; chmod u+x foo.app/foo&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; tree foo.app&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;foo.app&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;`-- foo&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We create an app bundle &lt;code&gt;foo.app&lt;/code&gt; that contains an executable &lt;em&gt;of the samename&lt;/em&gt;: if you name your app bundle &lt;code&gt;baz.app&lt;/code&gt;, the executable should be named&lt;code&gt;baz&lt;/code&gt;. You can now execute the bundle by typing &lt;code&gt;open foo.app&lt;/code&gt; in yourterminal, or double clicking on the bundle in Finder.&lt;/p&gt;&lt;p&gt;(Note that in order to display a Zenity calendar, I needed to make Bashinteractive. Leaving out the &lt;code&gt;-i&lt;/code&gt; flag would not display anything. I haven&amp;rsquo;tfigured out yet why that is the case, but do &lt;a href=&#34;https://relentlesscoding.com/pgp/public-key&#34;&gt;send me a note if youknow&lt;/a&gt;.)&lt;/p&gt;&lt;p&gt;If you want more control, add an icon, and so on, we need to get moreelaborate.&lt;/p&gt;&lt;h2 id=&#34;open-a-terminal-window-to-display-the-output-of-a-shell-script&#34;&gt;Open a Terminal Window to Display the Output of a Shell Script&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mkdir -p bar.app/Contents/&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;MacOS,Resources&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; cat &amp;gt; bar.app/Contents/MacOS/wrapper&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;#&lt;/span&gt;!/bin/bash&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;script_path=&amp;#34;$(dirname &amp;#34;$0&amp;#34;)&amp;#34;/actual-script&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;open -a Terminal &amp;#34;$script_path&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; cat &amp;gt; bar.app/Contents/MacOS/actual-script&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;#&lt;/span&gt;!/bin/bash&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;echo do something useful&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; chmod u+x bar.app/Contents/MacOS/&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;wrapper,actual-script&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here, we created a wrapper script that will open the Terminal and execute theactual, useful script such that any output would be visible to the user.&lt;/p&gt;&lt;p&gt;(Important: make sure the names of the files and directories are spelledcorrectly. For example, it&amp;rsquo;s &lt;code&gt;Contents&lt;/code&gt; (plural) and NOT &lt;code&gt;Content&lt;/code&gt;. Failing todo this will make your life miserable because the app won&amp;rsquo;t work in Finder andwon&amp;rsquo;t display a useful error message. Or, when &lt;code&gt;open&lt;/code&gt;ing it from your terminal,it will spit out weird errors like &amp;ldquo;executable is missing&amp;rdquo;.)&lt;/p&gt;&lt;p&gt;Then, add &lt;code&gt;&amp;lt;name&amp;gt;.app/Contents/Info.plist&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;!DOCTYPE plist PUBLIC &amp;#34;-//Apple Computer//DTD PLIST 1.0//EN&amp;#34; &amp;#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;#34;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;plist&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;version=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleName&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;Bar&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleExecutable&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;wrapper&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleIdentifier&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;com.relentlesscoding.Bar&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleVersion&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;1.2.3&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;CFBundleExecutable&lt;/code&gt; contains the name of the executable located in&lt;code&gt;&amp;lt;name&amp;gt;.app/Contents/MacOS&lt;/code&gt;. Reminder: do not forget to &lt;code&gt;chmod u+x &amp;lt;executable&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;code&gt;CFBundleIdentifier&lt;/code&gt; should contain a unique identifier for your app. Fail tomake this unique, and your app will replace some other app in your Launcher.&lt;/p&gt;&lt;p&gt;For good measure, I added &lt;code&gt;CFBundleVersion&lt;/code&gt; and put the app version in there.&lt;/p&gt;&lt;p&gt;Incidentally, you can &lt;a href=&#34;https://web.archive.org/web/20240224033253/https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html&#34;&gt;read all about these properties in Apple&amp;rsquo;sdocumentation&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&#34;add-an-icon&#34;&gt;Add an Icon&lt;/h2&gt;&lt;p&gt;If you search online, you&amp;rsquo;ll find recommendations for application icons andsizes. If you want to keep things simple, however, just download a 512x512 PNGicon from the internet, name it whatever you want, and put it in&lt;code&gt;&amp;lt;name&amp;gt;.app/Contents/Resources&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Then, modify &lt;code&gt;&amp;lt;name&amp;gt;.app/Contents/Info.plist&lt;/code&gt; to point to the icon. Here, Iadded an icon &lt;code&gt;bar.app/Contents/Resources/bar.png&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;!DOCTYPE plist PUBLIC &amp;#34;-//Apple Computer//DTD PLIST 1.0//EN&amp;#34; &amp;#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;#34;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;plist&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;version=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- ... snipped other tags... --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleIconFile&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;bar.png&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;tips&#34;&gt;Tips&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;You can check the validity of the &lt;code&gt;Info.plist&lt;/code&gt; file by running &lt;code&gt;plutil path/to/Info.plist&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; plutil bar.app/Contents/Info.plist&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;bar.app/Contents/Info.plist: OK&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;MacOS won&amp;rsquo;t immediately pick up changes you make to the app bundle. You canforce its hand by &lt;code&gt;touch&lt;/code&gt;ing the bundle:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; touch bar.app/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;All of this was tested on MacOS Sanoma.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</description>
     </item>
   
     <item>
       <title>Make passmenu Show GUI Pinentry When Using Pinentry Curses by Default</title>
       <link>https://relentlesscoding.com/posts/passmenu-show-gui-pinentry-different-from-default-curses/</link>
       <pubDate>Sun, 11 Oct 2020 11:28:23 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/passmenu-show-gui-pinentry-different-from-default-curses/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s take a look at how to use a GUI &lt;code&gt;pinentry&lt;/code&gt; for one program, and &lt;code&gt;curses&lt;/code&gt;for everything else.&lt;/p&gt;&lt;p&gt;I&amp;rsquo;m on Arch Linux using &lt;a href=&#34;https://i3wm.org/&#34;&gt;&lt;code&gt;i3wm&lt;/code&gt;&lt;/a&gt; as a window manager and &lt;a href=&#34;https://www.passwordstore.org/&#34;&gt;&lt;code&gt;pass&lt;/code&gt;&lt;/a&gt; as my passwordmanager. &lt;code&gt;pass&lt;/code&gt; comes with a handy script, &lt;code&gt;passmenu&lt;/code&gt;, that allows you to findyour password from within the status bar, &lt;code&gt;dmenu&lt;/code&gt;. This way you won&amp;rsquo;t have toopen a terminal every time you want to copy a password.&lt;/p&gt;&lt;p&gt;However, I had instructed &lt;code&gt;gpg-agent&lt;/code&gt; to use &lt;code&gt;/usr/bin/pinentry-curses&lt;/code&gt;. Thismeant that every time I invoked &lt;code&gt;passmenu&lt;/code&gt; and my PGP password was not alreadycached, nothing appeared to happen. (In fact, it would open the&lt;code&gt;pinentry-curses&lt;/code&gt; in a terminal not accessible to me and time out.)&lt;/p&gt;&lt;p&gt;So here is how to use a different &lt;code&gt;pinentry&lt;/code&gt; for &lt;code&gt;passmenu&lt;/code&gt; and &lt;code&gt;pass&lt;/code&gt;.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;First, create a wrapper script for &lt;code&gt;pinentry&lt;/code&gt; (&lt;code&gt;~/bin/pinentry-wrapper&lt;/code&gt;):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/usr/bin/env bash&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Defaults to Qt, with a choice of curses for selected programs&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# PINENTRY_USER_DATA is a GnuPG defined variable (see man gpg)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$PINENTRY_USER_DATA&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; in&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    gtk&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; /usr/bin/pinentry &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    *&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; /usr/bin/pinentry-curses &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;esac&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Make this script executable:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; chmod u+x ~/bin/pinentry-wrapper&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Instruct GnuGP to use your version of &lt;code&gt;pinentry&lt;/code&gt; (&lt;code&gt;~/.gnupg/gpg-agent.conf&lt;/code&gt;):&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pinentry-program /home/neftas/bin/pinentry-wrapper&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Restart the &lt;code&gt;gpg-agent&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; pkill -HUP gpg-agent&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Create a wrapper script for &lt;code&gt;passmenu&lt;/code&gt; (&lt;code&gt;~/bin/passmenu&lt;/code&gt;):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/usr/bin/env bash&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;PINENTRY_USER_DATA&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;gtk /usr/bin/passmenu &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Make it executable:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; chmod u+x ~/bin/passmenu&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Make sure &lt;code&gt;~/bin&lt;/code&gt; is searched first in your &lt;code&gt;PATH&lt;/code&gt; (put this in your &lt;code&gt;.bashrc&lt;/code&gt;):&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ export PATH=&amp;#34;~/bin:$PATH&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Check your work:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ command -v passmenu/home/neftas/bin/passmenu&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;a href=&#34;https://unix.stackexchange.com/questions/518331/can-i-configure-pass-to-always-use-pinentry-curses&#34;&gt;I&amp;rsquo;ve also posted this as an answer to a related question on the Unix &amp;amp; LinuxStackExchange page&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Why &lt;code&gt;PINENTRY_USER_DATA&lt;/code&gt; and not &lt;code&gt;MY_OWN_VAR&lt;/code&gt;? &lt;code&gt;man pgp&lt;/code&gt; tells us that:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;PINENTRY_USER_DATA    This value is passed via gpg-agent to pinentry. It is useful to convey extra    information to a custom pinentry.&lt;/code&gt;&lt;/pre&gt;</description>
     </item>
   
     <item>
       <title>Readline .inputrc Configuration Examples</title>
       <link>https://relentlesscoding.com/posts/readline-transformation/</link>
       <pubDate>Wed, 02 Sep 2020 19:10:49 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/readline-transformation/</guid>
       <description>&lt;p&gt;These are some of my favorite &lt;code&gt;readline&lt;/code&gt; tweaks.&lt;/p&gt;&lt;p&gt;To make changes to readline&amp;rsquo;s behavior, you have to put configuration in a filein your home directory called &lt;code&gt;.inputrc&lt;/code&gt;. To include commands from&lt;code&gt;/etc/inputrc&lt;/code&gt;, put the following first:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$include /etc/inputrc&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Variables to customize readline behavior can be set in this file by statementsof the form:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set variable-name value&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Restart your terminal emulator for the changes to take effect, or execute incurrent window:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;bind&lt;/span&gt; -f ~/.inputrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;briefly-move-to-opening-parenthesis-when-closing-parenthesis-is-typed&#34;&gt;Briefly Move to Opening Parenthesis When Closing Parenthesis Is Typed&lt;/h2&gt;&lt;p&gt;You want to make sure you have an even amount of opening and closingparentheses in your statements. By setting&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set blink-matching-paren On&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;the cursor will briefly move to the opening parenthesis.&lt;/p&gt;&lt;h2 id=&#34;display-possible-completions-with-different-colors-to-indicate-file-type&#34;&gt;Display Possible Completions With Different Colors to Indicate File Type&lt;/h2&gt;&lt;p&gt;Say, you&amp;rsquo;re looking for file starting with &lt;code&gt;.X&lt;/code&gt;, so you type:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; ls .X&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and then press &lt;kbd&gt;Tab&lt;/kbd&gt;. Setting&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set colored-stats On&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;will display the possible completions with a different color:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/colored-stats-on.png&#34; alt=&#34;Show different colors for file types on completion&#34; title=&#34;Show different colors for file types on completion&#34;&gt;&lt;/p&gt;&lt;h2 id=&#34;display-completions-one-per-line&#34;&gt;Display Completions One Per Line&lt;/h2&gt;&lt;p&gt;Instead of showing completions in columns, you can display completions one perline by setting:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set completion-display-width 0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The default is &lt;code&gt;-1&lt;/code&gt;, which means the value is ignored and completions areshown in columns.&lt;/p&gt;&lt;h2 id=&#34;ignore-case-on-completion&#34;&gt;Ignore Case on Completion&lt;/h2&gt;&lt;p&gt;You want to &lt;code&gt;cd&lt;/code&gt; into the folder &lt;code&gt;Documents&lt;/code&gt;, so you type:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; docu&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and press &lt;kbd&gt;Tab&lt;/kbd&gt;. Nothing happens. You want readline to complete itfor you ignoring the case:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set completion-ignore-case On&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;treat-hyphens-and-underscores-as-equivalent&#34;&gt;Treat Hyphens and Underscores as Equivalent&lt;/h2&gt;&lt;p&gt;If you enabled &lt;code&gt;completion-ignore-case&lt;/code&gt; (see above), you may also wantreadline to treat hyphens &lt;code&gt;-&lt;/code&gt; and underscores &lt;code&gt;_&lt;/code&gt; as equivalent whenperforming completion:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set completion-map-case On&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;replace-common-prefixes-of-certain-length-with-ellipsis&#34;&gt;Replace Common Prefixes of Certain Length with Ellipsis&lt;/h2&gt;&lt;p&gt;Instead of seeing the beginning of file names that you have already typed, youwant to focus on what differentiates the files:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; ls&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;file1 file2 file3 file4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; ls file&amp;lt;TAB&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;...1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;...2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;...3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;...4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When you type &lt;code&gt;ls file&lt;/code&gt; followed by &lt;kbd&gt;Tab&lt;/kbd&gt;, you will see the commonprefixes replaces by three dots.&lt;/p&gt;&lt;h2 id=&#34;disable-the-display-all-768-possibilities-y-or-no-on-completion&#34;&gt;Disable the &amp;ldquo;Display all 768 possibilities? (y or no)&amp;rdquo; on Completion&lt;/h2&gt;&lt;p&gt;Sometimes, when the amount of possible completions is greater than 100(default), readline will show a question whether or not you want to see allpossibilities. If you want to increase the threshold (default is 100), you canset:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set completion-query-items 1000&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;turn-current-command-into-a-comment&#34;&gt;Turn Current Command Into a Comment&lt;/h2&gt;&lt;p&gt;You&amp;rsquo;re typing a command and halfway realize you need to do something elsefirst. But you don&amp;rsquo;t want to throw away what you typed away. Then you can type&lt;kbd&gt;M&lt;/kbd&gt; + &lt;kbd&gt;#&lt;/kbd&gt; in emacs mode or &lt;kbd&gt;#&lt;/kbd&gt; in vi command mode(in vi insert mode you can still type &lt;kbd&gt;M&lt;/kbd&gt; + &lt;kbd&gt;#&lt;/kbd&gt; for the sameeffect).&lt;/p&gt;&lt;p&gt;You can change the string that is inserted when you press the insert-commentkey combo with &lt;code&gt;set comment-begin #&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;safe-pasting-with-bracketed-paste&#34;&gt;Safe Pasting with Bracketed Paste&lt;/h2&gt;&lt;p&gt;If you copy commands from the web, you might have noticed that when you copy anewline, Bash will interpret that newline as an Enter and execute whatever itis in your input buffer at the moment. This is unsafe, &lt;a href=&#34;https://thejh.net/misc/website-terminal-copy-paste&#34;&gt;because what you seemight not be what youcopy&lt;/a&gt;. You can be avoidedby setting:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set enable-bracketed-paste On&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This command puts an escape sequence around what you copy and paste, so Bashcan tell the difference between what you, the user types and what you copiedfrom somewhere.&lt;/p&gt;&lt;p&gt;The default is &lt;code&gt;Off&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;expand-tilde-to-home-directory&#34;&gt;Expand Tilde to Home Directory&lt;/h2&gt;&lt;p&gt;When you refer to the home directory of the user you&amp;rsquo;re currently logged inas, you most likely use tilde &lt;code&gt;~&lt;/code&gt; as in &lt;code&gt;cp ~/thesis.txt ~/Documents/&lt;/code&gt;.Setting&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set expand-tilde On&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;will always expand the tilde to the full path of your home directory when wordcompletion is attempted (meaning you need to press &lt;kbd&gt;Tab&lt;/kbd&gt; at somepoint).&lt;/p&gt;&lt;p&gt;The default is &lt;code&gt;Off&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;further-reading&#34;&gt;Further Reading&lt;/h2&gt;&lt;p&gt;See &lt;code&gt;man bash&lt;/code&gt; under &amp;ldquo;Readline Variables&amp;rdquo; for more information and even moreways to tweak your readline.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Sudo With Examples</title>
       <link>https://relentlesscoding.com/posts/sudo/</link>
       <pubDate>Sat, 15 Aug 2020 16:03:39 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/sudo/</guid>
       <description>&lt;p&gt;I&amp;rsquo;ve recently finished &amp;ldquo;Sudo Mastery&amp;rdquo; by &lt;a href=&#34;https://mwl.io/&#34;&gt;Michael W. Lucas&lt;/a&gt;.He&amp;rsquo;s a great and fun author that writes both technical guides and novels. Ibought his &lt;a href=&#34;https://mwl.io/fiction/crime&#34;&gt;&amp;quot;$ git commit murder&amp;quot;&lt;/a&gt; some years agoand was sold.&lt;/p&gt;&lt;p&gt;This post is basically a write-down of what I learned about &lt;code&gt;sudo&lt;/code&gt; from hisbook.&lt;/p&gt;&lt;h2 id=&#34;why-use-sudo&#34;&gt;Why Use &lt;code&gt;sudo&lt;/code&gt;?&lt;/h2&gt;&lt;p&gt;The most common reason to use &lt;code&gt;sudo&lt;/code&gt; is to not hand out the root password toevery person that logs on to a server. The problem with handing everyone theroot password is that:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;They would be able to arbitrarily execute every command as root (there&amp;rsquo;s nolimiting the commands that users can run as root).&lt;/li&gt;&lt;li&gt;There would be no audit trail other than that root executed these commands.&lt;/li&gt;&lt;li&gt;If you want to revoke someone&amp;rsquo;s root access, you would have to change the rootpassword and communicate the new password to the other root users.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;code&gt;sudo&lt;/code&gt; aims to solve these problems:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;You can specify which users can execute which commands (even includingparameters to commands).&lt;/li&gt;&lt;li&gt;&lt;code&gt;sudo&lt;/code&gt; can log all commands that are executed and notify when a user fails toauthenticate, tries to execute commands she is not allowed to and even whenshe tries but is not allowed to run &lt;code&gt;sudo&lt;/code&gt; at all.&lt;/li&gt;&lt;li&gt;To revoke sudo access, the sysadmin just removes the user from the sudoerspolicy.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Now, when you are the sole sysadmin of a server, &lt;code&gt;sudo&lt;/code&gt; might be overkill foryou. You can just use &lt;code&gt;su&lt;/code&gt; to become root and execute privileged commands.However, running all commands as root might be dangerous if you make mistakes.Having to type &lt;code&gt;sudo !!&lt;/code&gt; (&amp;ldquo;this time I mean it, dammit!&amp;rdquo;) might give you alittle time to think the command you&amp;rsquo;re about to execute over.&lt;/p&gt;&lt;h2 id=&#34;what-everybody-knows-about-sudo&#34;&gt;What Everybody Knows About &lt;code&gt;sudo&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;Run command as root:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; sudo docker ps&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run command as a different user:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; sudo -u john docker ps&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run command as a different group:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; sudo -g http less /var/log/nginx/access.log&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Become root by using your own password:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;sudo -i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;-i&lt;/code&gt; (long: &lt;code&gt;--login&lt;/code&gt;) makes sure you get an environment that is equal towhat you would get if you logged in as root. This means the environment fromthe user running &lt;code&gt;sudo&lt;/code&gt; is not kept, and &lt;code&gt;root&lt;/code&gt;&amp;rsquo;s login-specific resource files(&lt;code&gt;.profile&lt;/code&gt;, &lt;code&gt;.bash_profile&lt;/code&gt;, etc.) will be run. Specifying &lt;code&gt;-i&lt;/code&gt;/&lt;code&gt;--login&lt;/code&gt; isrecommended.&lt;/p&gt;&lt;h2 id=&#34;what-does-a-simple-sudo-policy-look-like&#34;&gt;What Does a Simple sudo Policy Look Like?&lt;/h2&gt;&lt;p&gt;Sudo stores its policy in &lt;code&gt;/etc/sudoers&lt;/code&gt;. It contains rules, defaults andaliases for hosts, users, run-as groups and commands.&lt;/p&gt;&lt;p&gt;Let&amp;rsquo;s take a look at a rule.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;stefan  www = (root:admin) NOPASSWD: /usr/bin/docker&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This policy specifies that the user &lt;code&gt;stefan&lt;/code&gt; can execute the command&lt;code&gt;/usr/bin/docker&lt;/code&gt; as user &lt;code&gt;root&lt;/code&gt; or group &lt;code&gt;admin&lt;/code&gt; on the machine with hostname&lt;code&gt;www&lt;/code&gt; and does not need to provide a password.&lt;/p&gt;&lt;p&gt;The syntax of a rule in sudoers looks like:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;user&amp;gt; &amp;lt;host&amp;gt; = (&amp;lt;user&amp;gt;:&amp;lt;group&amp;gt;) &amp;lt;tag&amp;gt; &amp;lt;commands&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A rule could be as simple as:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;stefan ALL = ALL&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This gives user &lt;code&gt;stefan&lt;/code&gt; carte blanche to execute whatever command tickles hisfancy.&lt;/p&gt;&lt;h3 id=&#34;instead-of-a-single-user-specify-a-list-of-users&#34;&gt;Instead of a Single User, Specify a List of Users&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;stefan,tom,rik www = /usr/bin/docker&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or you could define a &lt;code&gt;User_Alias&lt;/code&gt; and use that:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;User_Alias ADMIN = stefan,tom,rikADMIN www = /usr/bin/docker&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To permit all users to execute a command as root, use the catch-all &lt;code&gt;ALL&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&#34;instead-of-a-single-machine-specify-a-list-of-machines&#34;&gt;Instead of a Single Machine, Specify a List of Machines&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;stefan www1,www2,www3 = /usr/bin/docker&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or you could define a &lt;code&gt;Host_Alias&lt;/code&gt; and use that:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;User_Alias DBAS = henk,trudy,janeHost_Alias DBS = pg-db-tst,pg-db-acc,pg-db-proDBAS DBS = /usr/bin/psql&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This allows all DBAs (users &lt;code&gt;henk&lt;/code&gt;, &lt;code&gt;trudy&lt;/code&gt; and &lt;code&gt;jane&lt;/code&gt;) to execute&lt;code&gt;/usr/bin/psql&lt;/code&gt; on the machines &lt;code&gt;pg-db-tst&lt;/code&gt;, &lt;code&gt;pg-db-acc&lt;/code&gt; and &lt;code&gt;pg-db-pro&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Just as with users, you could specify &lt;code&gt;ALL&lt;/code&gt; as a catch-all to specify allmachines.&lt;/p&gt;&lt;h3 id=&#34;instead-of-a-single-run-as-user-or-group-specify-a-list-of-users-or-groups&#34;&gt;Instead of a Single Run-As User or Group, Specify a List of Users or Groups&lt;/h3&gt;&lt;p&gt;The run-as specification of a rule (the optional &lt;code&gt;(user:group)&lt;/code&gt; part),likewise, can be a list of users and groups:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;stefan ALL = (root,mike,jane:root,admin,log) ALL&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We could use a &lt;code&gt;Runas_Alias&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Runas_Alias = root,mike,jane,%root,%admin,%log&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To specify a group in a &lt;code&gt;Runas_Alias&lt;/code&gt;, prefix it with &lt;code&gt;%&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Again, &lt;code&gt;(ALL:ALL)&lt;/code&gt; could be used to indicate that the rules allows executinga command masquerading as anybody or as a member of any group. Use &lt;code&gt;(ALL)&lt;/code&gt;without the &lt;code&gt;:&amp;lt;group&amp;gt;&lt;/code&gt; to allow becoming just any user.&lt;/p&gt;&lt;h3 id=&#34;use-tags-to-change-sudo-with-regard-to-behavior-of-commands&#34;&gt;Use Tags to Change &lt;code&gt;sudo&lt;/code&gt; with Regard to Behavior of Commands&lt;/h3&gt;&lt;p&gt;&lt;code&gt;NOPASSWD:&lt;/code&gt; is called a &amp;ldquo;tag&amp;rdquo; and you can provide several of them on a singlerule:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;stefan  www = (root:admin) SETENV: NOPASSWD: /usr/bin/docker&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These tags change some aspect of the commands following it. In our example,&lt;code&gt;SETENV:&lt;/code&gt; allows the user to use the &lt;code&gt;-E&lt;/code&gt; flag (preventing its environmentvariables from being stripped by &lt;code&gt;sudo&lt;/code&gt;) and to keep environment variables set onthe command line:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo DOCKER_UID=$(id -u http) /usr/bin/docker &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Normally, these variables would be stripped (see &lt;code&gt;man sudoers&lt;/code&gt; for moredetails):&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sudo FOO=bar printenv FOOsudo: sorry, you are not allowed to set the following environment variables: FOO&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;NOPASSWD:&lt;/code&gt; tag allows the user to run the command without entering apassword.&lt;/p&gt;&lt;h3 id=&#34;provide-a-list-of-commands-in-a-single-rule-or-use-command-aliases&#34;&gt;Provide a List of Commands in a Single Rule or Use Command Aliases&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;stefan www = (root) /usr/bin/docker,/usr/bin/docker-compose&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Alternatively:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Cmnd_Alias DOCKER = /usr/bin/docker,/usr/bin/docker-composestefan www = (root) DOCKER&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;ground-rules&#34;&gt;Ground Rules&lt;/h2&gt;&lt;h3 id=&#34;use-visudo-to-edit-the-sudoers-policy&#34;&gt;Use &lt;code&gt;visudo&lt;/code&gt; to Edit the Sudoers Policy&lt;/h3&gt;&lt;p&gt;The policy sudo uses is stored in &lt;code&gt;/etc/sudoers&lt;/code&gt;. If you syntactically break thepolicy, &lt;code&gt;sudo&lt;/code&gt; won&amp;rsquo;t run. Therefore, always edit it using &lt;code&gt;visudo&lt;/code&gt;. &lt;code&gt;visudo&lt;/code&gt;copies the sudoers file to a temporary file and allows you to edit it using yourfavorite editor. When you close the temporary file, it validates the syntaxbefore overwriting &lt;code&gt;/etc/sudoers&lt;/code&gt;. If the syntax is invalid, it will show aprompt with the line number that contains the error and asks you what to do.This allows you to edit the file again (press &lt;kbd&gt;e&lt;/kbd&gt; and&lt;kbd&gt;Enter&lt;/kbd&gt;), fix the mistake and save the correct version.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Before starting to experiment with &lt;code&gt;sudo&lt;/code&gt;, make sure you can &lt;code&gt;su&lt;/code&gt; to &lt;code&gt;root&lt;/code&gt; soyou can fix your sudoers policy. It will be a nasty surprise when you screw upthe sudoers policy and only then find out you cannot become root through &lt;code&gt;su&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&#34;whitelist-not-blacklist&#34;&gt;Whitelist, Not Blacklist&lt;/h3&gt;&lt;p&gt;As is common with setting up firewalls, use a whitelist approach rather than ablacklist approach. It is hard to enumerate all the programs a user is &lt;em&gt;not&lt;/em&gt;allowed to execute. Rather, explicitly specify the programs a user needs to doher job.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;stefan ALL = /usr/bin/createdb,/usr/bin/dropdb,/usr/bin/psql&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Rather than&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;stefan ALL = !/usr/bin/passwd,!/usr/bin/mount,!/usr/bin/hostnamectl&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;set-noexec-to-prevent-shell-escapes&#34;&gt;Set &lt;code&gt;noexec&lt;/code&gt; to Prevent Shell Escapes&lt;/h3&gt;&lt;p&gt;If you allow users to execute editors and pagers as root, you basically givethem the keys to the kingdom. Why? Editors such as &lt;code&gt;vi&lt;/code&gt; and &lt;code&gt;vim&lt;/code&gt;, and pagerssuch as &lt;code&gt;less&lt;/code&gt; and &lt;code&gt;more&lt;/code&gt; allow users to execute commands in a subshell. Try ityourself: open a file with &lt;code&gt;less&lt;/code&gt;, type an exclamation mark &lt;code&gt;!&lt;/code&gt; and then &lt;code&gt;ls&lt;/code&gt;.It will list the files in the directory you executes &lt;code&gt;less&lt;/code&gt; from. These commandsare executed as the user that runs &lt;code&gt;less&lt;/code&gt;, which is &lt;code&gt;root&lt;/code&gt; if we had used &lt;code&gt;sudo less file.txt&lt;/code&gt;. If, instead of &lt;code&gt;ls&lt;/code&gt;, we had typed &lt;code&gt;bash&lt;/code&gt;, we would have a rootshell.&lt;/p&gt;&lt;p&gt;To prevent this from happening, you can set the &lt;code&gt;noexec&lt;/code&gt; parameter, either as adefault for everyone (recommended) or per-command:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Defaults!ALL noexec# alternativelystefan ALL = ALL, NOEXEC: /usr/bin/less&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What this does is prevent executables from running further commands itself.After setting this, executing &lt;code&gt;ls&lt;/code&gt; from within less now returns:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;!done  (press RETURN)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, you may find that by setting a global &lt;code&gt;noexec&lt;/code&gt; parameter you also can&amp;rsquo;trun &lt;code&gt;visudo&lt;/code&gt; anymore:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sudo visudovisudo: unable to run /usr/bin/vim: Permission deniedvisudo: /etc/sudoers.tmp unchanged&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Oops. &lt;code&gt;visudo&lt;/code&gt; tries to spawn a text editor, but it cannot run additionalcommands and so it fails. We need to exclude &lt;code&gt;visudo&lt;/code&gt; from the default rule:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Defaults!ALL noexecDefaults!/usr/bin/visudo !noexec&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, we might not want to give everybody access to &lt;code&gt;visudo&lt;/code&gt;. A good way tosolve this is with a command alias:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Defaults!ALL noexecCmnd_Alias MAYEXEC = /usr/bin/visudostefan ALL = ALL, EXEC: MAYEXEC&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;last-rule-matches-wins&#34;&gt;Last Rule Matches, Wins&lt;/h3&gt;&lt;p&gt;What does the following sudoers policy do:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;stefan ALL = !/usr/bin/docker,!/usr/bin/docker-composestefan ALL = ALL&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You&amp;rsquo;ve guessed it: it allows user &lt;code&gt;stefan&lt;/code&gt; to execute any command it wants. Thislooks straightforward, but don&amp;rsquo;t forget that a sudoers policy might contain&lt;em&gt;includes&lt;/em&gt;. For example, the sudoers policy shipped with Arch Linux at themoment of this writing contains this at the end:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#includedir /etc/sudoers.d&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sudo will read every file in the specified directory &lt;em&gt;in lexical order&lt;/em&gt;. Thismeans you need to understand what lexical ordering entails, or start every filewith a zero-padded number (&lt;code&gt;001-&lt;/code&gt;, &lt;code&gt;002-&lt;/code&gt;, etc.).&lt;/p&gt;&lt;p&gt;Every file could have rules that supersede the rules in &lt;code&gt;sudoers&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;use-sudoedit-not-sudo-ed&#34;&gt;Use &lt;code&gt;sudoedit&lt;/code&gt;, Not &lt;code&gt;sudo ed&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;Because editing a file as another user is such a common task, a conveniencecommand &lt;code&gt;sudoedit&lt;/code&gt; was created. This creates a temporary copy of the file thatcan be edited by the user invoking &lt;code&gt;sudo&lt;/code&gt; (no elevated privileges needed) andwhen the file gets closed, it replaces the old file with the edited copy. Thebenefit of using &lt;code&gt;sudoedit&lt;/code&gt; is that a user cannot abuse his elevated privilegeswith shell escapes, because the editor is run as an ordinary (non-privileged)user.&lt;/p&gt;&lt;h2 id=&#34;common-tasks-in-sudo&#34;&gt;Common Tasks in sudo&lt;/h2&gt;&lt;h3 id=&#34;change-default-sudo-editor&#34;&gt;Change Default sudo Editor&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Defaults editor = /usr/bin/ed&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can specify more than one by separating them with colons:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Defaults editor = /usr/bin/ed:/usr/bin/ex:/usr/bin/vi:/usr/bin/vim&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This preferences is used for both &lt;code&gt;visudo&lt;/code&gt; and &lt;code&gt;sudoedit&lt;/code&gt;, unless one of the&lt;code&gt;SUDO_EDITOR&lt;/code&gt;, &lt;code&gt;VISUAL&lt;/code&gt; or &lt;code&gt;EDITOR&lt;/code&gt; environment variables are set. Keep in mindthat &lt;code&gt;sudo&lt;/code&gt; strips these environment variables unless explicitly allowed in the&lt;code&gt;env_keep&lt;/code&gt; list.&lt;/p&gt;&lt;h3 id=&#34;extend-authentication-timeout-period&#34;&gt;Extend Authentication Timeout Period&lt;/h3&gt;&lt;p&gt;By default, &lt;code&gt;sudo&lt;/code&gt; will ask you to reauthenticate every five minutes. If you use&lt;code&gt;sudo&lt;/code&gt; a lot, you might want to increase this interval.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Defaults timestamp_timeout = 30  # minutes&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;authenticate-in-one-terminal-be-authenticated-everywhere&#34;&gt;Authenticate in One Terminal, Be Authenticated Everywhere&lt;/h3&gt;&lt;p&gt;By default, &lt;code&gt;sudo&lt;/code&gt; will require you to authenticate once per terminal. So, ifyou open two terminals, authenticate in the first, and then run &lt;code&gt;sudo&lt;/code&gt; in theother, you will be required to authenticate again. You can change this behaviorby setting &lt;code&gt;timestamp_type&lt;/code&gt; to &lt;code&gt;global&lt;/code&gt; (authenticate once and you&amp;rsquo;reauthenticated in all your user&amp;rsquo;s sessions on the same machine), &lt;code&gt;ppid&lt;/code&gt; (commandsthat run from the same parent process, say a shell, are all authenticated) or&lt;code&gt;tty&lt;/code&gt; (user&amp;rsquo;s sessions are authenticated separately per terminal, which is thedefault).&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Defaults timestamp_type = global&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;passwordless-sudo-for-some-commands&#34;&gt;Passwordless sudo For Some Commands&lt;/h3&gt;&lt;p&gt;To run some commands with elevated privileges without having to enter apassword:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;stefan ALL = NOPASSWD: /usr/bin/docker&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;keep-select-environment-variables&#34;&gt;Keep Select Environment Variables&lt;/h3&gt;&lt;p&gt;By default, &lt;code&gt;sudo&lt;/code&gt; removes most of the environment variables from the invokinguser&amp;rsquo;s environment, because they change the behavior of commands andinappropriate environment variables may break a system. To explicitly allowcertain variables:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Defaults env_keep += &amp;#34;EDITOR SYSTEMD_EDITOR&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note the double quotes around the environment variables. Also note that we&amp;rsquo;re&lt;em&gt;appending&lt;/em&gt; to the &lt;code&gt;env_keep&lt;/code&gt; list. Use &lt;code&gt;=&lt;/code&gt; instead of &lt;code&gt;+=&lt;/code&gt; to replace the list.&lt;/p&gt;&lt;p&gt;Run &lt;code&gt;sudo -V&lt;/code&gt; as root (&lt;code&gt;sudo sudo -V&lt;/code&gt;) to see which variables get stripped byyour installation of &lt;code&gt;sudo&lt;/code&gt; and which environment variables are already in&lt;code&gt;env_keep&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&#34;flush-authentication-timestamp&#34;&gt;Flush Authentication Timestamp&lt;/h3&gt;&lt;p&gt;When stepping away from your computer, you might want to flush your &lt;code&gt;sudo&lt;/code&gt;credentials, so that nobody will be able to run &lt;code&gt;sudo&lt;/code&gt; with your cachedcredentials. Run &lt;code&gt;sudo -K&lt;/code&gt; (long: &lt;code&gt;--remove-timestamp&lt;/code&gt;) to remove your cachedcredentials everywhere from the system.&lt;/p&gt;&lt;h2 id=&#34;intruder-detection-by-using-command-digests&#34;&gt;Intruder Detection by Using Command Digests&lt;/h2&gt;&lt;p&gt;&lt;code&gt;sudo&lt;/code&gt; provides functionality to check if the command you want to run has beentampered with. You can provide a digest of the command and &lt;code&gt;sudo&lt;/code&gt; will check itbefore running the command.&lt;/p&gt;&lt;p&gt;Generate a &lt;code&gt;sha224&lt;/code&gt; digest of &lt;code&gt;/usr/bin/docker&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; openssl sha224 -binary /usr/bin/docker &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; base64&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;EQy+FoFpPakt/QdMqJGJQLLeWMHmsFWG0N1A8Q==&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Put the name of the selected digest, a colon and the digest before the command:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;stefan ALL = sha224:EQy+FoFpPakt/QdMqJGJQLLeWMHmsFWG0N1A8Q== /usr/bin/docker&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When you update the packages on your system, the digest will change. If you planto use digests, you will probably want to write a script that updates thedigests after every upgrade.&lt;/p&gt;&lt;h2 id=&#34;replay-sudo-sessions-with-sudoreplay&#34;&gt;Replay sudo Sessions with &lt;code&gt;sudoreplay&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;To see what your users have been up to, you can replay entire &lt;code&gt;sudo&lt;/code&gt; sessionskeypress-by-keypress with &lt;code&gt;sudoreplay&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Defaults log_outputDefaults!/usr/bin/sudoreplay !log_outputCmnd_Alias      REBOOT = /sbin/halt, /sbin/reboot, /sbin/poweroffDefaults!REBOOT !log_output&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;log_output&lt;/code&gt; tells &lt;code&gt;sudo&lt;/code&gt; to log all output send to the terminal (if you changethis to &lt;code&gt;log_input&lt;/code&gt; it will also log everything the user types). The next linestell &lt;code&gt;sudo&lt;/code&gt; not to log the output of &lt;code&gt;sudoreplay&lt;/code&gt; itself and not to log areboots and shutdowns. Trying to log a reboot or shutdown can delay the process,because &lt;code&gt;sudo&lt;/code&gt; might try to write logs to a disk that has just been unmounted aspart of it.&lt;/p&gt;&lt;p&gt;&lt;code&gt;sudoreplay&lt;/code&gt; offers extensive functionality to find the session you&amp;rsquo;reinterested in. However, that is outside the scope of this post. Read &lt;code&gt;man sudoreplay&lt;/code&gt; for more information.&lt;/p&gt;&lt;h2 id=&#34;read-more&#34;&gt;Read More&lt;/h2&gt;&lt;p&gt;Read &lt;code&gt;man sudo&lt;/code&gt; (sudo command), &lt;code&gt;man sudoers&lt;/code&gt; (sudo policy) or if you&amp;rsquo;re lazy,&lt;a href=&#34;https://mwl.io/nonfiction/tools&#34;&gt;buy Michael W. Lucas&amp;rsquo; book&lt;/a&gt;!&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Read File into JSON List with jq</title>
       <link>https://relentlesscoding.com/posts/jq-read-file-into-json-list/</link>
       <pubDate>Wed, 22 Jul 2020 07:48:14 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/jq-read-file-into-json-list/</guid>
       <description>&lt;p&gt;You have a file and you want to convert the contents into a JSON list. How canwe leverage &lt;code&gt;jq&lt;/code&gt; to do that?&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; cat file&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;one&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;two&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;three&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; jq --null-input --raw-input &lt;span class=&#34;s1&#34;&gt;&amp;#39;[inputs]&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;one&amp;#34;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;two&amp;#34;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;three&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;--raw-input&lt;/code&gt; (short &lt;code&gt;-R&lt;/code&gt;) takes each line in the input as a string and passesit to the filter, instead of interpreting the file as containing JSON.&lt;/p&gt;&lt;p&gt;&lt;code&gt;--null-input&lt;/code&gt; (short &lt;code&gt;-n&lt;/code&gt;) is important:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; one two three &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; jq --raw-input &lt;span class=&#34;s1&#34;&gt;&amp;#39;[inputs]&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;two&amp;#34;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;three&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;--null-input&lt;/code&gt; doesn&amp;rsquo;t read the input at all. Instead, it will run the filterwith &lt;code&gt;null&lt;/code&gt; as input. In our case, &lt;code&gt;jq&lt;/code&gt; encounters &lt;code&gt;inputs&lt;/code&gt; here, which triggersan I/O operation that reads the contents of the file (or files) as strings(because of &lt;code&gt;--raw-input&lt;/code&gt;) into a list (because &lt;code&gt;inputs&lt;/code&gt; is surrounded by&lt;code&gt;[...]&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;Without &lt;code&gt;--null-input&lt;/code&gt;, &lt;code&gt;jq&lt;/code&gt; takes the first line of the file, and passes thatto the filter. The filter discards this input (we don&amp;rsquo;t use the identityoperator &lt;code&gt;.&lt;/code&gt;) and proceeds to read all remaining inputs (&lt;code&gt;two&lt;/code&gt; and &lt;code&gt;three&lt;/code&gt;).Obviously, this is not what we want.&lt;/p&gt;&lt;p&gt;Knowing this, we &lt;em&gt;could&lt;/em&gt; do the following (but really shouldn&amp;rsquo;t):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; one two three &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; jq --raw-input &lt;span class=&#34;s1&#34;&gt;&amp;#39;[.] + [inputs]&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;one&amp;#34;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;two&amp;#34;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;three&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;remove-empty-strings-from-output&#34;&gt;Remove Empty Strings from Output&lt;/h2&gt;&lt;p&gt;If you get empty strings for whatever reason in your output, you can removethem:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; one two three &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; jq -n -R &lt;span class=&#34;s1&#34;&gt;&amp;#39;[inputs]&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;&amp;#34;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;one&amp;#34;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;two&amp;#34;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;three&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; one two three &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; jq -n -R &lt;span class=&#34;s1&#34;&gt;&amp;#39;[inputs | select(length &amp;gt; 0)]&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;one&amp;#34;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;two&amp;#34;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  &amp;#34;three&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Pipe to Multiple Commands with Bash</title>
       <link>https://relentlesscoding.com/posts/bash-pipe-to-multiple-commands/</link>
       <pubDate>Mon, 13 Jul 2020 18:27:25 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/bash-pipe-to-multiple-commands/</guid>
       <description>&lt;p&gt;Sometimes it&amp;rsquo;s handy to filter the output of a command (with &lt;code&gt;grep&lt;/code&gt;, forexample) while still having the column names (the first line) available. How dowe go about that?&lt;/p&gt;&lt;p&gt;One example of this would be when filtering the output of &lt;code&gt;ps&lt;/code&gt;. What does thefollowing output mean, after all?&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; ps -ef &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep logd&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    0   124     1   0  7:45AM ??         0:01.46 /usr/sbin/syslogd&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    0   146     1   0  7:45AM ??         0:09.44 /usr/libexec/logd&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt; 1000 17263 14631   0 10:36AM ttys010    0:00.01 grep --color=auto logd&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It would be much more readable if the output would be:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; UID   PID  PPID   C STIME   TTY           TIME CMD   0   124     1   0  7:45AM ??         0:01.46 /usr/sbin/syslogd   0   146     1   0  7:45AM ??         0:09.44 /usr/libexec/logd1000 17263 14631   0 10:36AM ttys010    0:00.01 grep --color=auto logd&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, the correct answer to this is:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ps -ef | tee &amp;gt;(sed -n 1p) &amp;gt;(grep logd) &amp;gt;/dev/null&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here, &lt;code&gt;tee&lt;/code&gt; takes a variable number of files to write the same output to. We useprocess substitution (see &lt;code&gt;man bash&lt;/code&gt;) to make &lt;code&gt;tee&lt;/code&gt; write to the named pipes,which are a kind of file. &lt;code&gt;tee&lt;/code&gt; also prints its input to standard out, and wesuppress that by redirecting it to &lt;code&gt;/dev/null&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Because the list of commands inside process substitution run asynchronously, toensure &lt;code&gt;sed -n 1p&lt;/code&gt; runs before &lt;code&gt;grep logd&lt;/code&gt;, we could insert &lt;code&gt;sleep 0.1s&lt;/code&gt; beforerunning &lt;code&gt;grep logd&lt;/code&gt;. That should give &lt;code&gt;sed -n 1p&lt;/code&gt; enough time to print its lineand will make the output more reliable.&lt;/p&gt;&lt;p&gt;Interestingly, &lt;code&gt;&amp;gt;(head -1)&lt;/code&gt; instead of &lt;code&gt;&amp;gt;(sed -n 1p)&lt;/code&gt; would &lt;em&gt;not&lt;/em&gt; work underLinux, because &lt;code&gt;head&lt;/code&gt; closes its stdin as soon as it has read enough bytes toprint the first line. At that point, &lt;code&gt;tee&lt;/code&gt; quits with an exit status of 141 (128+ 13 for &lt;code&gt;SIGPIPE&lt;/code&gt; &amp;ndash; see &lt;code&gt;man bash&lt;/code&gt; and grep for &amp;ldquo;Simple Commands&amp;rdquo; for anexplanation). Instead, we use a command which is guaranteed to read the entireoutput &lt;code&gt;tee&lt;/code&gt; feeds it.&lt;/p&gt;&lt;p&gt;In the rest of this post I will look at another &amp;ldquo;solution&amp;rdquo; to this problem thatuses group commands, and show why it is incorrect.&lt;/p&gt;&lt;h2 id=&#34;the-wrong-answer&#34;&gt;The Wrong Answer&lt;/h2&gt;&lt;p&gt;I read the comments below the answer on ServerFault on &lt;a href=&#34;https://serverfault.com/a/608041/328318&#34;&gt;&amp;ldquo;How to grep ps outputwith headers&amp;rdquo;&lt;/a&gt; which suggested wecould use &lt;code&gt;ps -ef | { head -1; grep logd; }&lt;/code&gt; to solve this problem(incidentally, &lt;code&gt;{ cmd; cmd; }&lt;/code&gt; is called a &amp;ldquo;group command&amp;rdquo;).  If we wouldn&amp;rsquo;tknow from our previous run what the correct output would look like, this wouldseem to work:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ps -ef | { head -1; grep logd; }  UID   PID  PPID   C STIME   TTY           TIME CMD    0   146     1   0  7:45AM ??         0:09.48 /usr/libexec/logd&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But notice two of the three processes are missing. What gives?&lt;/p&gt;&lt;p&gt;The key is understanding that &lt;code&gt;cmd1 | { cmd2; cmd3; }&lt;/code&gt; does not provide a copyof the standard output of &lt;code&gt;cmd1&lt;/code&gt; to &lt;code&gt;cmd2&lt;/code&gt; and &lt;code&gt;cmd3&lt;/code&gt; (so that both &lt;code&gt;cmd2&lt;/code&gt; and&lt;code&gt;cmd3&lt;/code&gt; would be working with the same input). Rather, the input is shared:whatever is consumed by &lt;code&gt;cmd2&lt;/code&gt; is no longer available for &lt;code&gt;cmd3&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;We can prove this very easily. If we substitute &lt;code&gt;cmd2&lt;/code&gt; with something thatconsumes all input, such as &lt;code&gt;grep&lt;/code&gt; (which has to consume every line in the inputlooking for a match), the input for the next command should be empty:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ printf &amp;#39;%s\n&amp;#39; foo bar baz | { grep nomatch; wc -c; }0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Indeed, we see that &lt;code&gt;wc -c&lt;/code&gt; has zero bytes to work with.&lt;/p&gt;&lt;p&gt;OK, you might think, but &lt;code&gt;head -1&lt;/code&gt; only consumes the first line (up until thefirst newline) of the input, right? So why &lt;em&gt;exactly&lt;/em&gt; doesn&amp;rsquo;t &lt;code&gt;ps -ef | { head -1; grep logd; }&lt;/code&gt; work?  As it turns out, &lt;code&gt;head&lt;/code&gt; reads more than just up to thefirst newline:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ printf &amp;#39;%s\n&amp;#39; foo bar baz | { head -1; wc -c; }0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;How much does it read by default (I&amp;rsquo;ve tested with &lt;code&gt;head&lt;/code&gt; from &lt;code&gt;GNU coreutils 8.32&lt;/code&gt;)?&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ &amp;lt; /dev/urandom tr -d -c &amp;#39;[[:alnum:]]&amp;#39; | head -c 10000 &amp;gt; urandom.txt$ cat urandom.txt | { head -1 &amp;gt;/dev/null; wc -c; }8976$ echo &amp;#39;10000 - 8976&amp;#39; | bc1024&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It reads the first 1024 bytes that are no longer available to any othercommands that are following. This means that you shouldn&amp;rsquo;t rely on groupcommands to work with a single input.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Maven&#39;s Versions Plugin Updates All Your Dependencies With a Single Command</title>
       <link>https://relentlesscoding.com/posts/mavens-versions-plugin-updates-all-your-dependencies-with-a-single-command/</link>
       <pubDate>Mon, 11 May 2020 19:25:43 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/mavens-versions-plugin-updates-all-your-dependencies-with-a-single-command/</guid>
       <description>&lt;p&gt;It’s a good habit to use the latest versions of the dependencies youuse. Not only because the updated version might contain new features orbetter performance (one can hope), but also because bugs are fixed andsecurity issues are tackled. If you go about it by hand, however,updating dependencies is a boring and tedious task. Luckily for us,there is the Maven &lt;code&gt;versions&lt;/code&gt; plugin to help us.&lt;/p&gt;&lt;h2 id=&#34;table-of-contents&#34;&gt;Table of Contents&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;#check-which-dependenciespluginsproperties-need-updating&#34;&gt;Check Which Dependencies/Plugins/Properties NeedUpdating&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#update-parent&#34;&gt;Update Parent&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#update-to-latest-versions&#34;&gt;Update To Latest Versions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#update-a-particular-property-with-bounds&#34;&gt;Update a Particular Property withBounds&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#conclusion&#34;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#reference&#34;&gt;Reference&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;check-which-dependenciespluginsproperties-need-updating&#34;&gt;Check Which Dependencies/Plugins/Properties Need Updating&lt;/h2&gt;&lt;p&gt;If you need a report on which dependencies, plugins or properties usedfor versioning can be upgraded, run one of these:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;versions:display-dependency-updates&lt;/code&gt; scans a project’s dependenciesand produces a report of those dependencies which have newer versionsavailable.&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;versions:display-plugin-updates&lt;/code&gt; scans a project’s plugins andproduces a report of those plugins which have newer versionsavailable, taking care of Maven version prerequisites.&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;versions:display-property-updates&lt;/code&gt; scans a project and produces areport of those properties which are used to control artifact versionsand which properties have newer versions available.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&#34;update-parent&#34;&gt;Update Parent&lt;/h2&gt;&lt;p&gt;If you have a parent section, the following command updates the versionto the latest available:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mvn versions:update-parent&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;update-to-latest-versions&#34;&gt;Update To Latest Versions&lt;/h2&gt;&lt;p&gt;To upgrade all your dependencies to the latest versions, use:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mvn versions:use-latest-versions&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;update-a-particular-property-with-bounds&#34;&gt;Update a Particular Property with Bounds&lt;/h2&gt;&lt;p&gt;To upgrade dependencies that get their version from the &lt;code&gt;&amp;lt;properties&amp;gt;&lt;/code&gt;section of your POM to version that has bounds:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mvn versions:update-property &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;&lt;span class=&#34;go&#34;&gt;    -Dproperty=&amp;#39;cucumber.version&amp;#39; \&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    -DnewVersion=&amp;#39;(,4.99]&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this case, the lower bound is unspecified (and exclusive asindicated by the &lt;code&gt;(&lt;/code&gt;) and the upper bound in &lt;code&gt;4.99&lt;/code&gt; inclusive. You can&lt;a href=&#34;https://www.mojohaus.org/versions-maven-plugin/update-property-mojo.html#newVersion&#34;&gt;tweak &lt;code&gt;-DnewVersion&lt;/code&gt; to yourliking&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;The Maven &lt;code&gt;versions&lt;/code&gt; plugin makes it easy for us the see whichdependencies are outdated and can even update the dependencies for uswith a single command. After updating, don’t forget to run your testsuite and run the application to make sure nothing broke!&lt;/p&gt;&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;&lt;p&gt;This has been tested on MacOS with the following Maven and &lt;code&gt;versions&lt;/code&gt;versions:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mvn --version&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Maven home: /usr/local/Cellar/maven/3.6.3_1/libexec&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Java version: 13.0.2, vendor: N/A, runtime: /usr/local/Cellar/openjdk/13.0.2+8_2/libexec/openjdk.jdk/Contents/Home&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Default locale: en_NL, platform encoding: UTF-8&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;OS name: &amp;#34;mac os x&amp;#34;, version: &amp;#34;10.15.4&amp;#34;, arch: &amp;#34;x86_64&amp;#34;, family: &amp;#34;mac&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mvn help:describe -Dplugin&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;versions -Dminimal &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s1&#34;&gt;&amp;#39;^Version: &amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Version: 2.7&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The documentation can be found&lt;a href=&#34;https://www.mojohaus.org/versions-maven-plugin/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Oracle Sqlplus in a Small Docker Container</title>
       <link>https://relentlesscoding.com/posts/oracle-sqlplus-in-a-small-docker-container/</link>
       <pubDate>Mon, 11 May 2020 19:19:22 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/oracle-sqlplus-in-a-small-docker-container/</guid>
       <description>&lt;p&gt;When you only need Sql*Plus, it&amp;rsquo;s good to have a small Docker image that youcan use. This post will look at how to create this Docker image.&lt;/p&gt;&lt;h2 id=&#34;table-of-contents&#34;&gt;Table of Contents&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;#introduction&#34;&gt;Introduction&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#how-to-build-your-own-image&#34;&gt;How to Build Your Own Image&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#optional-add-tnsnames.ora-to-the-image&#34;&gt;Optional: Add &lt;code&gt;tnsnames.ora&lt;/code&gt; to theImage&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;&lt;p&gt;Some time back I wrote a blog post about &lt;a href=&#34;https://relentlesscoding.com/posts/oracle-sqlplus-cheatsheet/&#34;&gt;SQL*Pluscommands&lt;/a&gt;.  To run those commands, I woulduse a 15 GiB image of Oracle XE (Express Edition) just to be able to use acommand-line tool. Today I stumbled upon Oracle’s Docker images, that are awhole lot smaller.&lt;/p&gt;&lt;h2 id=&#34;how-to-build-your-own-image&#34;&gt;How to Build Your Own Image&lt;/h2&gt;&lt;p&gt;Oracle provides &lt;a href=&#34;https://github.com/oracle/docker-images&#34;&gt;&lt;code&gt;Dockerfile&lt;/code&gt;s on its GitHubpage&lt;/a&gt;. I’m interested in justSQL*Plus so I can change my password on an Oracle database and maybeexecute some simple queries. I took a look at &lt;a href=&#34;https://github.com/oracle/docker-images/blob/master/OracleInstantClient/dockerfiles/19/Dockerfile&#34;&gt;this&lt;code&gt;Dockerfile&lt;/code&gt;&lt;/a&gt;,and modified it slightly to include only what I need:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;oraclelinux:7-slim&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ARG&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;release&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;19&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ARG&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;update&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt;  yum -y install oracle-release-el7 &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;     yum-config-manager --enable ol7_oracle_instantclient &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;     yum -y install oracle-instantclient&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;release&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;.&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;update&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;-basic &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;        oracle-instantclient&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;release&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;.&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;update&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;-sqlplus &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;     rm -rf /var/cache/yum&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;CMD&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;sqlplus&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-v&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Build the image and tag it as &lt;code&gt;sqlplus&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; docker build -t sqlplus .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The image it generates is pretty small, weighing around 370 MB. (OK, animage based on Alpine should be smaller, but Oracle requires &lt;code&gt;glibc&lt;/code&gt; andnot &lt;code&gt;musl libc&lt;/code&gt;.) You can create a container and run a SQL*Plus commandwith:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; docker run -it sqlplus user/password@db.domain.tld:1521/FOO&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;optional-add-tnsnamesora-to-the-image&#34;&gt;Optional: Add &lt;code&gt;tnsnames.ora&lt;/code&gt; to the Image&lt;/h2&gt;&lt;p&gt;In Oracle, a file name &lt;code&gt;tnsnames.ora&lt;/code&gt; contains the connection stringsfor databases. They allow you to connect to a database using &lt;code&gt;sqlplus&lt;/code&gt;without specifying the hostname:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; sqlplus user/passsword@FOO&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Add the &lt;code&gt;tnsnames.ora&lt;/code&gt; to the image by putting the following between the&lt;code&gt;RUN&lt;/code&gt; and &lt;code&gt;CMD&lt;/code&gt; directive in the &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;COPY&lt;/span&gt; ./tnsnames.ora /root&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ENV&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;TNS_ADMIN&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/root&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Automatic Rollback of Transactions in Spring Tests</title>
       <link>https://relentlesscoding.com/posts/automatic-rollback-of-transactions-in-spring-tests/</link>
       <pubDate>Sun, 01 Mar 2020 11:11:02 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/automatic-rollback-of-transactions-in-spring-tests/</guid>
       <description>&lt;p&gt;Put&lt;a href=&#34;https://docs.spring.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/transaction/annotation/Transactional.html&#34;&gt;&lt;code&gt;@Transactional&lt;/code&gt;&lt;/a&gt;on your test class or methods and &lt;em&gt;test-managed transactions&lt;/em&gt; willautomatically be rollbacked.&lt;/p&gt;&lt;h2 id=&#34;table-of-contents&#34;&gt;Table of Contents&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;#the-antipattern&#34;&gt;The Antipattern&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#difference-between-test--spring--and-application-managed-transactions&#34;&gt;Difference Between Test-, Spring- and Application-ManagedTransactions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#what-does-transactional-do&#34;&gt;What Does &lt;code&gt;@Transactional&lt;/code&gt; Do?&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#beware-only-test-managed-transactions-are-rolled-back&#34;&gt;Beware: Only &lt;strong&gt;Test-Managed Transactions&lt;/strong&gt; Are RolledBack&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#set-up-or-tear-down-outside-of-a-transaction&#34;&gt;Set Up or Tear Down Outside of aTransaction&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#spring-boot-version&#34;&gt;Spring Boot Version&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;the-antipattern&#34;&gt;The Antipattern&lt;/h2&gt;&lt;p&gt;When looking at system test in Spring, it is not uncommon to see thispattern:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-kotlin&#34; data-lang=&#34;kotlin&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;org.junit.jupiter.api.Assertions.assertEquals&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;org.junit.jupiter.api.extension.ExtendWith&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;org.junit.jupiter.api.Test&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;org.springframework.beans.factory.annotation.Autowired&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;org.springframework.boot.test.context.SpringBootTest&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;org.springframework.test.context.junit.jupiter.SpringExtension&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@ExtendWith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SpringExtension&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@SpringBootTest&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;TransactionalTestApplicationTests&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nd&#34;&gt;@Autowired&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;lateinit&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;py&#34;&gt;mapper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Mapper&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;fun&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;`insert user into db, verify and do cleanup`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;py&#34;&gt;user&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mapper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;createUser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;username&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;assertEquals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;mapper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;deleteUser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;username&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The cleanup is done manually, at the end of the test. This introduces aproblem: what if the test fails? Now, the cleanup statement at the endis no longer executed. A common solution is to introduce an &lt;code&gt;@After&lt;/code&gt;method that does the cleanup in a more or less crude manner:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-kotlin&#34; data-lang=&#34;kotlin&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@After&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fun&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;cleanup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mapper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;deleteAllUsers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This pattern is in fact unnecessary. Spring provides the&lt;code&gt;@Transactional&lt;/code&gt; annotation that will do an automatic cleanup afterevery &lt;code&gt;@Test&lt;/code&gt; method:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-kotlin&#34; data-lang=&#34;kotlin&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@ExtendWith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SpringExtension&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@SpringBootTest&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Transactional&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;TransactionalTestApplicationTests&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nd&#34;&gt;@Autowired&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;lateinit&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;py&#34;&gt;mapper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Mapper&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;fun&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;`insert user into db, verify and do cleanup`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;cm&#34;&gt;/* code */&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;difference-between-test--spring--and-application-managed-transactions&#34;&gt;Difference Between Test-, Spring- and Application-Managed Transactions&lt;/h2&gt;&lt;p&gt;A &lt;strong&gt;test-managed transaction&lt;/strong&gt; is one that is managed by&lt;code&gt;TransactionalTestExecutionListener&lt;/code&gt; (or programmatically by&lt;a href=&#34;https://docs.spring.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/test/context/transaction/TestTransaction.html&#34;&gt;&lt;code&gt;TestTransaction&lt;/code&gt;&lt;/a&gt;).A test-managed transaction differs from &lt;strong&gt;Spring-managed transactions&lt;/strong&gt;(transactions directly managed by Spring within the &lt;code&gt;ApplicationContext&lt;/code&gt;that is loaded for the test) and &lt;strong&gt;application-managed transactions&lt;/strong&gt;(transactions programmatically managed within the application code thatis executed by the test):&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Spring-managed and application-managed transactions will typicallyparticipate in test-managed transactions; however, caution should betaken if Spring-managed or application-managed transactions areconfigured with any propagation type other than&lt;a href=&#34;https://docs.spring.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/transaction/annotation/Propagation.html#REQUIRED&#34;&gt;&lt;code&gt;REQUIRED&lt;/code&gt;&lt;/a&gt;or&lt;a href=&#34;https://docs.spring.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/transaction/annotation/Propagation.html#SUPPORTS&#34;&gt;&lt;code&gt;SUPPORTS&lt;/code&gt;&lt;/a&gt;.— &lt;a href=&#34;https://docs.spring.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/test/context/transaction/TransactionalTestExecutionListener.html&#34;&gt;Documentation for&lt;code&gt;TransactionalTestExecutionListener&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&#34;what-does-transactional-do&#34;&gt;What Does &lt;code&gt;@Transactional&lt;/code&gt; Do?&lt;/h2&gt;&lt;p&gt;Running tests while starting up the Spring application context, a&lt;a href=&#34;https://docs.spring.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/test/context/transaction/TransactionalTestExecutionListener.html&#34;&gt;&lt;code&gt;TransactionalTestExecutionListener&lt;/code&gt;&lt;/a&gt;is automatically configured. This listener provides support forexecuting tests with test-managed transactions. It notices the&lt;code&gt;@Transactional&lt;/code&gt; on the test class or individual &lt;code&gt;@Test&lt;/code&gt; methods andcreates a new transaction that is then automatically rolled back aftertest completion. The default is rollback. This behavior can be changed,however, by putting&lt;a href=&#34;https://docs.spring.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/test/annotation/Commit.html&#34;&gt;&lt;code&gt;@Commit&lt;/code&gt;&lt;/a&gt;or&lt;a href=&#34;https://docs.spring.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/test/annotation/Rollback.html&#34;&gt;&lt;code&gt;@Rollback&lt;/code&gt;&lt;/a&gt;at the class or method level. If you need more control over yourtransactions, you can use the static methods in&lt;a href=&#34;https://docs.spring.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/test/context/transaction/TestTransaction.html&#34;&gt;&lt;code&gt;TestTransaction&lt;/code&gt;&lt;/a&gt;.They allow you to start and end transactions, flag them for commit orrollback or check the transaction status.&lt;/p&gt;&lt;h2 id=&#34;beware-only-test-managed-transactions-are-rolled-back&#34;&gt;Beware: Only &lt;strong&gt;Test-Managed Transactions&lt;/strong&gt; Are Rolled Back&lt;/h2&gt;&lt;p&gt;Transactions are not rolled back when the code that is invoked does notinteract with the database. An example would be writing an end-to-endtest that uses &lt;code&gt;RestTemplate&lt;/code&gt; to make an HTTP request to some endpointthat then makes a modification in the database. Since the &lt;code&gt;RestTemplate&lt;/code&gt;does not interact with the database (it just creates and sends an HTTPrequest), &lt;code&gt;@Transactional&lt;/code&gt; will not rollback anything that is an effectof that HTTP request. This is because a separate transaction, one notcontrolled by the test, will be started.&lt;/p&gt;&lt;h2 id=&#34;set-up-or-tear-down-outside-of-a-transaction&#34;&gt;Set Up or Tear Down Outside of a Transaction&lt;/h2&gt;&lt;p&gt;You can annotate a &lt;code&gt;public void&lt;/code&gt; method with&lt;a href=&#34;https://docs.spring.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/test/context/transaction/BeforeTransaction.html&#34;&gt;&lt;code&gt;@BeforeTransaction&lt;/code&gt;&lt;/a&gt;or&lt;a href=&#34;https://docs.spring.io/spring/docs/5.2.4.RELEASE/javadoc-api/org/springframework/test/context/transaction/AfterTransaction.html&#34;&gt;&lt;code&gt;@AfterTransaction&lt;/code&gt;&lt;/a&gt;.This indicates to Spring that this method should be run before or aftera test method is run within a transaction.&lt;/p&gt;&lt;h2 id=&#34;spring-boot-version&#34;&gt;Spring Boot Version&lt;/h2&gt;&lt;p&gt;I wrote this code with Spring Boot version 2.2.5.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>SQL Injection in Bash Scripts</title>
       <link>https://relentlesscoding.com/posts/sql-injection-in-bash-scripts/</link>
       <pubDate>Sat, 29 Feb 2020 16:36:49 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/sql-injection-in-bash-scripts/</guid>
       <description>&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;&lt;p&gt;When working on &lt;a href=&#34;https://relentlesscoding.com/posts/mariadb-mysql-password-exposure-in-bash/&#34;&gt;making a small application with a ZenityGUI&lt;/a&gt;,I ask the user for input. I need to somehow sanitize this input, or Iwill leave the gates wide open for two problems:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Sql_injection&#34;&gt;SQL injection&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Code_injection&#34;&gt;Command injection&lt;/a&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;In this blog post, I’m going to look at a solution for the firstproblem.&lt;/p&gt;&lt;h2 id=&#34;disclaimer&#34;&gt;Disclaimer&lt;/h2&gt;&lt;p&gt;Of course, you shouldn&amp;rsquo;t write SQL queries in a programming languagethat doesn&amp;rsquo;t provide a way to supply parameterized queries. Also, when auser is executing a shell script themselves on their local machine, SQLand command injection become academic, because the user already has theright to modify the database or to execute random Bash commands. Itwould only be problematic if the script would be executed by an HTTPserver that can reached by the internet at large. But in that case, youwouldn&amp;rsquo;t be writing a Bash script, you would use a proper programminglanguage, right?&lt;/p&gt;&lt;h2 id=&#34;solving-sql-injection-the-normal-way-parameterized-queries&#34;&gt;Solving SQL Injection The Normal Way: Parameterized Queries&lt;/h2&gt;&lt;p&gt;The solution for SQL injection, is, of course, parameterized queries.You send a query to the database and leave placeholders (AKA “bindvariables”) open, that take the form of a question mark or a namedparameter:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AND&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;password&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AND&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;password&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;password&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The parameters are sent separately to the RDBMS, and are bound to acertain type (varchar, integer, etc) and thus cannot cause unintendedside effects anymore.&lt;/p&gt;&lt;h2 id=&#34;however-bash-doesnt-let-you-do-this&#34;&gt;However, Bash Doesn&amp;rsquo;t Let You Do This&lt;/h2&gt;&lt;p&gt;In Bash, however, it’s hard to use this if the provided executabledoesn&amp;rsquo;t accept parameters. Postgres’ &lt;code&gt;psql&lt;/code&gt; takes a &lt;code&gt;-v&lt;/code&gt; option, andallows you to write:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; psql -u user -h host -v &lt;span class=&#34;s1&#34;&gt;&amp;#39;name=qwerty&amp;#39;&lt;/span&gt; -v &lt;span class=&#34;s1&#34;&gt;&amp;#39;password=12345&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;&lt;span class=&#34;go&#34;&gt;    &amp;#39;SELECT * FROM user WHERE name = :name AND password = :password;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;MariaDB/MySQL, however, provides nothing of the kind.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;IFS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;|&amp;#39;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;read&lt;/span&gt; -r description amount &amp;lt; &amp;lt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;zenity --forms &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;        --add-entry description &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;        --add-entry amount &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;        --text &lt;span class=&#34;s1&#34;&gt;&amp;#39;Please specify a description and amount&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# assume env variable MYSQL_PWD is set&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mysql -u user -h host &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -e &lt;span class=&#34;s2&#34;&gt;&amp;#34;INSERT INTO advance VALUES (&amp;#39;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$description&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#39;, &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$amount&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;);&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The code fragment above leaves us completely exposed to SQL injection.&lt;/p&gt;&lt;h2 id=&#34;solving-sql-injection-in-bash&#34;&gt;Solving SQL Injection In Bash&lt;/h2&gt;&lt;p&gt;We could &lt;a href=&#34;https://en.wikipedia.org/wiki/Base64&#34;&gt;base64&lt;/a&gt; encode the userdata and use a function on the RDBMS to decode from base64.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;IFS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;|&amp;#39;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;read&lt;/span&gt; -r us_description us_amount &amp;lt; &amp;lt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;zenity --forms &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;        --add-entry Description &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;        --add-entry Amount &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;        --text &lt;span class=&#34;s1&#34;&gt;&amp;#39;Please specify a description and amount&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;description_base64&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; -n &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$us_description&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; base64&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;parse_float &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$us_amount&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; &amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Invalid float&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; 1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# assume MYSQL_PWD is set&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mysql -u user -h host &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -e &lt;span class=&#34;s2&#34;&gt;&amp;#34;INSERT INTO advance VALUES (FROM_BASE64(&amp;#39;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$description_base64&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#39;), &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$amount&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;));&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;why-does-this-work&#34;&gt;Why Does This Work?&lt;/h2&gt;&lt;p&gt;You might think to yourself: why does this actually work? Doesn&amp;rsquo;t thefunction get expanded and you end up with the same problem? Let’s figureout how a SQL statement gets processed by the RDBMS.&lt;/p&gt;&lt;p&gt;When the RDBMS receives the SQL statement, it is handled by a SQL interpreter.The interpreter consists of two parts: a parser and an optimizer. First, theparser checks for syntax errors. If the syntax is correct, it will checkwhether the statement is semantically correct: for example, do all referencedtables and columns exist? The parser separates the pieces of a SQL statementinto a data structure that other routines can process.  (As we’ll see, this isimportant: after parsing, SQL injection is no longer possible.) After theinterpreter is done with the statement, the statement is handed to theoptimizer. The optimizer tries to determine how to get the best performance.The result is an execution plan, which is fed to a code generator and finallyrun by the database processor.&lt;/p&gt;&lt;p&gt;Schematically:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/sql-processing-query-600px.png&#34; alt=&#34;SQL statement processing&#34;&gt;(Read&lt;a href=&#34;https://docs.oracle.com/database/121/TGSQL/tgsql_sqlproc.htm#TGSQL178&#34;&gt;here&lt;/a&gt;how Oracle processes statements). A function is evaluated during theexecution phase. The parser has no problem with a valid, existingfunction that promises to return a value that is expected in thatposition. The optimizer’s task is only to determine at what moment &lt;em&gt;inthe execution phase&lt;/em&gt; the function is best executed, but also doesn&amp;rsquo;tevaluate the function. We can prove all this to ourselves by calling afunction that will throw an error:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;MariaDB [foo]&amp;gt; \W&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Show warnings enabled.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;MariaDB [huishouden]&amp;gt; SELECT FROM_BASE64(&amp;#39;!&amp;#39;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;+------------------+&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;| FROM_BASE64(&amp;#39;!&amp;#39;) |&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;+------------------+&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;| NULL             |&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;+------------------+&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;1 row in set, 1 warning (0.004 sec)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;go&#34;&gt;Warning (Code 1958): Bad base64 data as position 0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;MariaDB [foo]&amp;gt; -- no warning will show because function is not evaluated&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;MariaDB [foo]&amp;gt; SELECT FROM_BASE64(&amp;#39;!&amp;#39;) WHERE 1 = 0;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Empty set (0.003 sec)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Calling &lt;code&gt;FROM_BASE64&lt;/code&gt; with an invalid base64 string &lt;code&gt;!&lt;/code&gt; results in aruntime exception. When we add a predicate in the &lt;code&gt;WHERE&lt;/code&gt; clause thatalways fails (&lt;code&gt;1 = 0&lt;/code&gt;), the query gets executed nonetheless, showing usthat MariaDB/MySQL will only evaluate the function at runtime. Atruntime, the return value of &lt;code&gt;FROM_BASE64&lt;/code&gt; cannot be used anymore toalter the SQL statement, because that stage is long past. At this pointin time, the return value is harmless.&lt;/p&gt;&lt;h2 id=&#34;software-i-used&#34;&gt;Software I used&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; uname -a&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Linux cirrus7 5.5.3-arch1-1 #1 SMP PREEMPT Tue, 11 Feb 2020 15:35:41 +0000 x86_64 GNU/Linux&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mysql --version&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;mysql  Ver 15.1 Distrib 10.4.12-MariaDB, for Linux (x86_64) using readline 5.1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; zenity --version&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;3.32.0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Skipping or Selectively Running Tests With Maven</title>
       <link>https://relentlesscoding.com/posts/skipping-or-selectively-running-tests-with-maven/</link>
       <pubDate>Tue, 25 Feb 2020 20:59:17 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/skipping-or-selectively-running-tests-with-maven/</guid>
       <description>&lt;p&gt;You might not always want to execute all tests. Sometimes you only want to runa single unit test, or only integrations tests that start with &lt;code&gt;Foo&lt;/code&gt;. Let&amp;rsquo;s lookat how to make this happen in Maven.&lt;/p&gt;&lt;h2 id=&#34;table-of-contents&#34;&gt;Table of Contents&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;#skip-unit-or-integration-tests&#34;&gt;Skip Unit or Integration Tests&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#execute-a-single-unit-or-integration-test-class&#34;&gt;Execute a Single Unit or Integration TestClass&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#execute-a-single-unit-or-integration-test-within-a-test-class&#34;&gt;Execute a Single Unit or Integration Test Within a TestClass&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#further-reading&#34;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;skip-unit-or-integration-tests&#34;&gt;Skip Unit or Integration Tests&lt;/h2&gt;&lt;p&gt;Skip the execution of unit tests:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mvn compile -DskipTests&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will still compile the unit tests, but not run them. To skip bothcompilation and execution, use &lt;code&gt;-Dmaven.test.skip&lt;/code&gt;. Note that thissetting is also honored by the Failsafe plugin. Skip executingintegration tests:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mvn verify -DskipITs&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;execute-a-single-unit-or-integration-test-class&#34;&gt;Execute a Single Unit or Integration Test Class&lt;/h2&gt;&lt;p&gt;Execute a single unit test class with Surefire:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mvn test -Dtest=MyUnitTest&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In a multi-module project, we need to add &lt;code&gt;-DfailIfNoTests=false&lt;/code&gt;,otherwise a module will fail where a class with that name is notavailable. Also note that we do not execute integration tests because weinstruct Maven to stop after the &lt;code&gt;test&lt;/code&gt; phase. To run a single unit testclass and skip all integration tests, add &lt;code&gt;-DskipITs&lt;/code&gt;. Execute a singleintegration test class with Failsafe:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mvn verify -Dit.test=MyIntegrationTest&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can guess by now, this will run the single integration test bythe name of &lt;code&gt;MyIntegrationTest&lt;/code&gt;, &lt;em&gt;and will also run all unit tests&lt;/em&gt;. Toprevent this, you can add &lt;code&gt;-DskipTests&lt;/code&gt;. Again, don’t forget to set&lt;code&gt;-DfailIfNoTests&lt;/code&gt; if you run Maven on a multi-module project.&lt;/p&gt;&lt;h2 id=&#34;execute-a-single-unit-or-integration-test-within-a-test-class&#34;&gt;Execute a Single Unit or Integration Test Within a Test Class&lt;/h2&gt;&lt;p&gt;Both the Surefire and Failsafe Maven plugins allow you to go crazy intheir syntax for filtering which individual test classes or methodsshould be run. Specifying the parameters &lt;code&gt;-Dtest&lt;/code&gt; and &lt;code&gt;-Dit.test&lt;/code&gt; willoverride all defined include all defined includes and excludes from your&lt;code&gt;pom.xml&lt;/code&gt;. Maven will create an include that looks like&lt;code&gt;**/${test}.java&lt;/code&gt;. We have seen how we can run single test classes byspecifying the class name after &lt;code&gt;-Dtest&lt;/code&gt; or &lt;code&gt;-Dit.test&lt;/code&gt;. The next mostsimple format (since version 2.7.3) takes the form of&lt;code&gt;ClassName#methodName&lt;/code&gt;, so if you have a test class named&lt;code&gt;OrderTest.java&lt;/code&gt; and a unit test method &lt;code&gt;testEmptyOrderShouldThrow&lt;/code&gt;, youcan run this test method by itself, excluding all other test by typing:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mvn test -Dtest=OrderTest#testEmptyOrderShouldThrow&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Complexer syntax with both glob and regex patterns is possible sinceversion 2.19. The documentation gives this example:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mvn test &amp;#39;-Dtest=???Test, !Unstable*, pkg/**/Ci*leTest.java,&amp;gt; *Test#test*One+testTwo?????, #fast*+slowTest&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let’s break this down. As you can see, we specify multiple patterns in asingle parameter (to prevent shell expansion, they are put inside singlequotes):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;???Test&lt;/code&gt; will run any test that has a name that starts with anythree characters and ends with &lt;code&gt;Test&lt;/code&gt; (&lt;code&gt;?&lt;/code&gt; is a glob charactermeaning any single character).&lt;/li&gt;&lt;li&gt;&lt;code&gt;!Unstable*&lt;/code&gt; will not run any test that starts with &lt;code&gt;Unstable&lt;/code&gt; (theinitial &lt;code&gt;!&lt;/code&gt; negates the pattern).&lt;/li&gt;&lt;li&gt;&lt;code&gt;pkg/**/Ci*leTest.java&lt;/code&gt; will run any test that has &lt;code&gt;pkg&lt;/code&gt; somewherein its package name, followed by any number of other child packagenames, while the class name should start with &lt;code&gt;Ci&lt;/code&gt; followed by zeroor more wildcard characters and ending with &lt;code&gt;leTest.java&lt;/code&gt;(&lt;code&gt;pkg/CileTest.java&lt;/code&gt; matches, as does&lt;code&gt;foo/bar/pkg/baz/CiqwertyleTest.java&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;&lt;code&gt;*Test#test*One+testTwo?????&lt;/code&gt; will run any test whose class nameends in &lt;code&gt;Test&lt;/code&gt; and whose method name matches &lt;code&gt;test&lt;/code&gt; + any amount ofcharacters + &lt;code&gt;One&lt;/code&gt; or &lt;code&gt;testTwo&lt;/code&gt; + any five characters. So this willrun two test methods in every test class called &lt;code&gt;*Test&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;#fast*+slowTest&lt;/code&gt; will run the methods &lt;code&gt;fast*&lt;/code&gt; and &lt;code&gt;slowTest&lt;/code&gt; whereever there are found.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We can even have more fun by throwing in regular expressions:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mvn test &amp;#39;-Dtest=Basic*, !%regex[.*.Unstable.*],&amp;gt; !%regex[.*.MyTest.class#one.*|two.*], %regex[#fast.*|slow.*]&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;!%regex[.*.Unstable.*]&lt;/code&gt; will not run any tests that contain&lt;code&gt;Unstable&lt;/code&gt; somewhere in their path.&lt;/li&gt;&lt;li&gt;&lt;code&gt;!%regex[.*.MyTest.class#one.*|two.*]&lt;/code&gt; will run methods named &lt;code&gt;one&lt;/code&gt;and &lt;code&gt;two&lt;/code&gt; in a class called &lt;code&gt;MyTest&lt;/code&gt;. We see that when using globs,we can use forward slashes to delimit packages. However, when usingregex, we use dots.&lt;/li&gt;&lt;li&gt;&lt;code&gt;%regex[#fast.*|slow.*]&lt;/code&gt; will run the test methods &lt;code&gt;fast&lt;/code&gt; and &lt;code&gt;slow&lt;/code&gt;wherever they are found. Note that (at least in version &lt;code&gt;3.0.0-M4&lt;/code&gt;),the trailing &lt;code&gt;.*&lt;/code&gt; wildcard pattern is not needed.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;further-reading&#34;&gt;Further Reading&lt;/h2&gt;&lt;p&gt;You can read more about Surefire and Failsafe from the comfort of yourterminal:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mvn surefire:help -Ddetail$ mvn failsafe:help -Ddetail&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also read the Surefire and Failsafe online documentation:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://maven.apache.org/surefire/maven-surefire-plugin/index.html&#34;&gt;Surefire&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://maven.apache.org/surefire/maven-failsafe-plugin/index.html&#34;&gt;Failsafe&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</description>
     </item>
   
     <item>
       <title>Grokking Time Zones in Oracle&#39;s DATE and TIMESTAMP</title>
       <link>https://relentlesscoding.com/posts/grokking-time-zones-in-oracles-date-and-timestamp/</link>
       <pubDate>Sat, 22 Feb 2020 18:22:14 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/grokking-time-zones-in-oracles-date-and-timestamp/</guid>
       <description>&lt;p&gt;This post talks a little bit about &lt;code&gt;DATE&lt;/code&gt; and &lt;code&gt;TIMESTAMP&lt;/code&gt; in Oracle and some oftheir pitfalls. TL;DR: Representing dates and timestamps correctly is a very hardproblem (&lt;a href=&#34;https://www.youtube.com/watch?v=-5wpm-gesOY&#34;&gt;required viewing&lt;/a&gt;).&lt;/p&gt;&lt;h2 id=&#34;table-of-contents&#34;&gt;Table of Contents&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;#intro&#34;&gt;Intro&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#dates&#34;&gt;&lt;code&gt;DATE&lt;/code&gt;s&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#plain-timestamps&#34;&gt;Plain &lt;code&gt;TIMESTAMP&lt;/code&gt;s&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#timestamp-with-local-time-zone&#34;&gt;&lt;code&gt;TIMESTAMP WITH LOCAL TIME ZONE&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#timestamp-with-time-zone&#34;&gt;&lt;code&gt;TIMESTAMP WITH TIME ZONE&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#conclusion&#34;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#using-oracle-dates-in-java&#34;&gt;Using Oracle &lt;code&gt;DATE&lt;/code&gt;s in Java&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#sources&#34;&gt;Sources&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;&lt;p&gt;When I was tasked with putting some data in BigQuery, Google Cloud’sdata warehouse implementation, I had to look at the data we currentlystore in our local Oracle database. I found that we were using &lt;code&gt;DATE&lt;/code&gt;for fields that store “moments in time”. Normally, I would expect Unixtimestamps here, or some other data type that contains the time zone.&lt;/p&gt;&lt;p&gt;Naturally, I complained about this, but people were quick to point outthat it doesn&amp;rsquo;t matter, because you should relate the &lt;code&gt;DATE&lt;/code&gt; to the timezone of the database. Oracle, it turns out, is tracking two time zones:the time zone of the database itself:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dbtimezone&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dual&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- Europe/Amsterdam&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and the time zone of the client session:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sessiontimezone&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dual&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- Europe/Moscow&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(&lt;a href=&#34;https://relentlesscoding.com/posts/oracle-sqlplus-cheatsheet/&#34;&gt;See here how to update these timezones&lt;/a&gt;.)&lt;/p&gt;&lt;p&gt;I was warned that Oracle might change the query results based on eitherof these time zones, meaning when you insert a data type related todates or time, you might get back an altered representation based on thetime zone of the database or the session of the client.&lt;/p&gt;&lt;p&gt;This did not put my mind at ease at all. So, let’s take a look at whatsome of the common types relating to date and time are in Oracle, andwhen we should be careful with the query results.&lt;/p&gt;&lt;h2 id=&#34;dates&#34;&gt;&lt;code&gt;DATE&lt;/code&gt;s&lt;/h2&gt;&lt;p&gt;A &lt;code&gt;DATE&lt;/code&gt; is a calendar date with a wallclock time &lt;em&gt;without&lt;/em&gt; a time zone.If you put &lt;code&gt;2020-02-20 09:11:43&lt;/code&gt; in the database, you will alwaysretrieve the same exact datetime, no matter whether you’re in Amsterdamor New York. (It is the equivalent of a&lt;a href=&#34;https://stackoverflow.com/questions/32437550/whats-the-difference-between-instant-and-localdatetime&#34;&gt;&lt;code&gt;java.time.LocalDateTime&lt;/code&gt;&lt;/a&gt;,if that’s your thing.)&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sessiontimezone&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dual&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- Europe/Amsterdam&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;def_time&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;users&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;foo@example.com&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 2020-02-20 09:04:50&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ALTER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SESSION&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;time_zone&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;UTC&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;def_time&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;users&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;foo@example.com&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 2020-02-20 09:04:50&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;plain-timestamps&#34;&gt;Plain &lt;code&gt;TIMESTAMP&lt;/code&gt;s&lt;/h2&gt;&lt;p&gt;A &lt;code&gt;TIMESTAMP&lt;/code&gt; (without a time zone) is the same beast as a &lt;code&gt;DATE&lt;/code&gt;,except that you can specify times up to a fraction of a second (a &lt;code&gt;DATE&lt;/code&gt;needs to be rounded to the nearest second).&lt;/p&gt;&lt;h2 id=&#34;timestamp-with-local-time-zone&#34;&gt;&lt;code&gt;TIMESTAMP WITH LOCAL TIME ZONE&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;Say, you store the value &lt;code&gt;2020-02-20 09:11:43 +01:00&lt;/code&gt;. A client with a&lt;code&gt;sessiontimezone&lt;/code&gt; of &lt;code&gt;Europe/Amsterdam&lt;/code&gt; in the winter will retrieve thisexact value whether or not they retrieve it from an Amsterdam, Sydney orNew York database. Another client with a session time zone that is setto &lt;code&gt;America/New_York&lt;/code&gt;, however, will get the value &lt;code&gt;2020-02-20 03:11:43 -05:00&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;So: &lt;code&gt;LOCAL&lt;/code&gt; means it adjusts the retrieved value to your session timezone.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ALTER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SESSION&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;time_zone&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Europe/Moscow&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- offset +03:00&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_char&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;ttz&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;yyyy-MM-dd HH24:MI:SS&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- timestamp with local, New York time zone&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;CAST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_timestamp_tz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2020-02-20 09:11:43 -05:00&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                              &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;yyyy-MM-dd HH24:MI:SS TZH:TZM&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TIMESTAMP&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WITH&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;LOCAL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TIME&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ZONE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;ttz&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dual&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 2020-02-20 17:11:43&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;See also &lt;a href=&#34;https://www.oracletutorial.com/oracle-date-functions/oracle-dbtimezone/&#34;&gt;thistutorial&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&#34;timestamp-with-time-zone&#34;&gt;&lt;code&gt;TIMESTAMP WITH TIME ZONE&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;This data type stores the time zone together with the datetime anddisplays it independently from the session time zone of the client.&lt;/p&gt;&lt;p&gt;If we store a value &lt;code&gt;2020-02-20 09:11:43 +01:00&lt;/code&gt; in Amsterdam and copythis to a database in New York, this is the value that is returned toclients in both Amsterdam and New York regardless of their session timezone.&lt;/p&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;&lt;code&gt;DATE&lt;/code&gt;s and plain &lt;code&gt;TIMESTAMP&lt;/code&gt;s don’t store a time zone and queries willalways return the exact same value that was inserted regardless of&lt;code&gt;dbtimezone&lt;/code&gt; or &lt;code&gt;sessiontimezone&lt;/code&gt;. This makes them safe choices if yousomehow don’t care about time zones.&lt;/p&gt;&lt;p&gt;&lt;code&gt;TIMESTAMP WITH LOCAL TIME ZONE&lt;/code&gt; and &lt;code&gt;TIMESTAMP WITH TIME ZONE&lt;/code&gt; &lt;em&gt;do&lt;/em&gt;store time zones. A &lt;code&gt;TIMESTAMP WITH LOCAL TIME ZONE&lt;/code&gt; will be representeddifferently depending on your session time zone. A &lt;code&gt;TIMESTAMP WITH TIME ZONE&lt;/code&gt; will not be represented differently; it will just include anoffset.&lt;/p&gt;&lt;p&gt;You might now always want to use &lt;code&gt;TIMESTAMP WITH TIME ZONE&lt;/code&gt;, butapparently, &lt;a href=&#34;https://tonyhasler.wordpress.com/2010/09/04/tonys-tirade-against-timestamp-with-time-zone/&#34;&gt;they have problems of theirown&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Falling back to &lt;code&gt;DATE&lt;/code&gt;s, however, is problematic because of DaylightSaving Time (DST) in the fall. When you store the current datetime inthe fall, and you go through the dance of setting all clocks back from3:00 to 2:00, you have no way of knowing at what exact point in time therow was inserted. This is bad if you rely on this for anything crucial,such as forensics (“so the hacker copied the entire database at 2:30,but was that before or after we reset our clocks?”).&lt;/p&gt;&lt;h2 id=&#34;using-oracle-dates-in-java&#34;&gt;Using Oracle &lt;code&gt;DATE&lt;/code&gt;s in Java&lt;/h2&gt;&lt;p&gt;As for me, having to deal with &lt;code&gt;DATE&lt;/code&gt;s without an explicit time zone,this means that the application inserting/retrieving these datetimeswill need to decide how to interpret them. In my Java application, Iwill map them to&lt;a href=&#34;https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html&#34;&gt;&lt;code&gt;java.time.LocalDateTime&lt;/code&gt;&lt;/a&gt;,add a time zone and convert them to&lt;a href=&#34;https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html&#34;&gt;&lt;code&gt;java.time.Instant&lt;/code&gt;&lt;/a&gt;s:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Instant&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;defTimeInstant&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getDefTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;atZone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ZoneId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Europe/Moscow&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;toInstant&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;user.getDefTime()&lt;/code&gt; returns a &lt;code&gt;LocalDateTime&lt;/code&gt; indicating when the userwas created. We can give a &lt;code&gt;LocalDateTime&lt;/code&gt; a time zone by invoking&lt;code&gt;atZone(ZoneId)&lt;/code&gt; on it, returing a&lt;a href=&#34;https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html&#34;&gt;&lt;code&gt;ZonedDateTime&lt;/code&gt;&lt;/a&gt;.Once we have converted the datetime to an actual point on the timeline(no longer being some kind of abstract datetime without a timezone), wecan convert it to an &lt;code&gt;Instant&lt;/code&gt; by invoking &lt;code&gt;toInstant()&lt;/code&gt; on the&lt;code&gt;ZonedDateTime&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;sources&#34;&gt;Sources&lt;/h2&gt;&lt;p&gt;&lt;a href=&#34;https://docs.oracle.com/en/database/oracle/oracle-database/20/cncpt/tables-and-table-clusters.html#GUID-F5FBE34D-911C-4818-9D25-64B2F1613A9B&#34;&gt;Oracle documentation on datetime datatypes&lt;/a&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Oracle Sqlplus Cheat Sheet</title>
       <link>https://relentlesscoding.com/posts/oracle-sqlplus-cheatsheet/</link>
       <pubDate>Sat, 22 Feb 2020 16:30:58 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/oracle-sqlplus-cheatsheet/</guid>
       <description>&lt;p&gt;In this post, I&amp;rsquo;m going to aggregate all those Oracle commands that I can neverremember but are very useful to have somewhere written down.&lt;/p&gt;&lt;h2 id=&#34;table-of-contents&#34;&gt;Table of Contents&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;#intro&#34;&gt;Intro&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#getting-help-with-sqlplus&#34;&gt;Getting Help With &lt;code&gt;sqlplus&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#connecting-to-an-oracle-database-using-sqlplus&#34;&gt;Connecting to an Oracle Database UsingSQL*Plus&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#show-the-connected-user&#34;&gt;Show the Connected User&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#show-all-schemas&#34;&gt;Show All Schema’s&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#show-all-tablesviews-in-a-particular-schema&#34;&gt;Show All Tables/Views in a ParticularSchema&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#describe-a-table&#34;&gt;Describe a Table&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#show-ddl-of-a-table&#34;&gt;Show DDL of a Table&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#show-the-privileges-of-your-user&#34;&gt;Show the Privileges of YourUser&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#get-more-decent-output-from-a-command&#34;&gt;Get More Decent Output From aCommand&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#get-and-alter-database-timezones&#34;&gt;Get and Alter DatabaseTimezones&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#select-date-rows-in-the-last-hour&#34;&gt;Select &lt;code&gt;DATE&lt;/code&gt; Rows in the LastHour&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#working-around-oracle-not-having-a-limit&#34;&gt;Working Around Oracle Not Having a&lt;code&gt;LIMIT&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#setting-new-password-for-user&#34;&gt;Setting New Password for User&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;#show-output-from-script&#34;&gt;Show Output from Script&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;&lt;p&gt;Last week, I suddenly had to work with an Oracle database again. Inormally use &lt;a href=&#34;https://www.jetbrains.com/datagrip&#34;&gt;Intellij’s DataGrip&lt;/a&gt;to connect to databases. I tried it this time, and I found I could notconnect to the schema I wanted: the schema just turned up empty. Ofcourse, everybody will recommend you use Oracle’s SQL Developer with anyOracle database you have to touch. So, after trying &lt;code&gt;brew search sqldeveloper&lt;/code&gt; (yes, I’m on a Mac at work), coming up empty, reading&lt;a href=&#34;https://github.com/Homebrew/homebrew-cask/issues/16454&#34;&gt;this caskrequest&lt;/a&gt; andfeeling the anticipation of endless frustration grow inside me, I wentto Oracle’s web site to see if I could download the program. I can,except that they want me to turn in a DNA sample first:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/oracle-create-account-872x1024.png&#34; alt=&#34;Oracle’s create-new-accountpage&#34;&gt;&lt;/p&gt;&lt;p&gt;Of course, faking those kind of details is not impossible, but thehassle of going through something like that just for a lousy program soI can just run a couple of lousy queries puts me off. Luckily, I had aDocker container with Oracle XE lying around. It includes SQL*Plus, the“venerable” command-line tool provided by Oracle to query and administerOracle databases. Being a Arch Linux user with a predilection foranything with a command-line interface, it was not too bad, but gettingthe output formatted so that I could actually make sense of it requiredmore effort than most users are willing to put up with. So how do youfind your way around when you have SQL*Plus and no clue how it works?How do you find the schema’s you want, the tables you need to query? Howcan you check your privileges? This post aims to be a cheat sheet so youcan find your way around. Keep in mind that the results you’re gettingdepend on the privileges your user has. Getting an empty result &lt;em&gt;doesnot mean&lt;/em&gt; that the database object does not exist. Rather, it meansyou’re a lesser god.&lt;/p&gt;&lt;h2 id=&#34;getting-help-with-sqlplus&#34;&gt;Getting Help With &lt;code&gt;sqlplus&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;&lt;code&gt;sqlplus&lt;/code&gt; does not have a man page, but provides help when you pass&lt;code&gt;-h/-help&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sqlplus -h&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;connecting-to-an-oracle-database-using-sqlplus&#34;&gt;Connecting to an Oracle Database Using SQL*Plus&lt;/h2&gt;&lt;p&gt;The basic syntax to connect as user &lt;code&gt;alice&lt;/code&gt; with password &lt;code&gt;qwerty&lt;/code&gt; to adatabase &lt;code&gt;FOO&lt;/code&gt; which is located on &lt;code&gt;db.domain.tld&lt;/code&gt; and listens on port&lt;code&gt;1521&lt;/code&gt; (default port) is:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sqlplus alice/qwerty@db.domain.tld:1521/FOO&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;show-the-connected-user&#34;&gt;Show the Connected User&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SHOW&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;USER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;SHOW&lt;/code&gt; command lets you look at the current state of your SQL*Plusenvironment: &lt;code&gt;SHOW ALL&lt;/code&gt; shows all settings.&lt;/p&gt;&lt;h2 id=&#34;show-all-schemas&#34;&gt;Show All Schema’s&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dba_users&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Return only non-empty schema’s (excluding most users who never createdany object in the database):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dba_users&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;EXISTS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- filter users without database objects&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dba_objects&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;owner&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Excluding Oracle’s built-in schema’s:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dba_users&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;default_tablespace&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;IN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;SYSTEM&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;SYSAUX&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://stackoverflow.com/a/4833503/2521769&#34;&gt;Source&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&#34;show-all-tablesviews-in-a-particular-schema&#34;&gt;Show All Tables/Views in a Particular Schema&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;table_name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;all_tables&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;owner&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;MYSCHEMA&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Related: find all views:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;view_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;all_views&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;owner&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;MYSCHEMA&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;describe-a-table&#34;&gt;Describe a Table&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;DESCRIBE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;table&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;show-ddl-of-a-table&#34;&gt;Show DDL of a Table&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dbms_metadata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_ddl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;VIEW&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;USERS&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;MYSCHEMA&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dual&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;where the first argument is the type of object (e.g. &lt;code&gt;&#39;TABLE&#39;&lt;/code&gt;,&lt;code&gt;&#39;VIEW&#39;&lt;/code&gt;, &lt;code&gt;&#39;TRIGGER&#39;&lt;/code&gt;), the second is the name of the object, and thethird the schema where the object is defined.&lt;/p&gt;&lt;h2 id=&#34;show-the-privileges-of-your-user&#34;&gt;Show the Privileges of Your User&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;USER_SYS_PRIVS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;USER_TAB_PRIVS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;USER_ROLE_PRIVS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://stackoverflow.com/a/9811709/2521769&#34;&gt;Source&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&#34;get-more-decent-output-from-a-command&#34;&gt;Get More Decent Output From a Command&lt;/h2&gt;&lt;p&gt;If you want SQL*Plus to truncate the value of a column:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WRAP&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;OFF&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;otherwise it will allow the column to wrap to the next line (default&lt;code&gt;ON&lt;/code&gt;). Suppress all headings, page breaks, titles, the initial blankline and other formatting information:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PAGESIZE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://docs.oracle.com/cd/B19306_01/server.102/b14357/ch12040.htm&#34;&gt;Source&lt;/a&gt;Now, if only Oracle would support &lt;code&gt;\G&lt;/code&gt; for vertical output…&lt;/p&gt;&lt;h2 id=&#34;get-and-alter-database-timezones&#34;&gt;Get and Alter Database Timezones&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dbtimezone&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dual&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ALTER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DATABASE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dbtimezone&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Europe/Amsterdam&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sessiontimezone&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dual&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ALTER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SESSION&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sessiontimezone&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Europe/Amsterdam&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;select-date-rows-in-the-last-hour&#34;&gt;Select &lt;code&gt;DATE&lt;/code&gt; Rows in the Last Hour&lt;/h2&gt;&lt;p&gt;Table &lt;code&gt;t&lt;/code&gt; has a column &lt;code&gt;c_date&lt;/code&gt; of type &lt;code&gt;DATE&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;table&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c_date&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sysdate&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This works because you can subtract a fraction from a &lt;code&gt;DATE&lt;/code&gt; type wherethe fraction is interpreted as a fraction of a day (&lt;code&gt;1&lt;/code&gt; is an entireday, &lt;code&gt;0.5&lt;/code&gt; is 12 hours, etc.).&lt;/p&gt;&lt;h2 id=&#34;working-around-oracle-not-having-a-limit&#34;&gt;Working Around Oracle Not Having a &lt;code&gt;LIMIT&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;Yes, Oracle does not have a &lt;code&gt;LIMIT&lt;/code&gt; keyword, so this idiom will quicklybecome ingrained in your muscle memory:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- actual query&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;firstName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lastName&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;users&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ROWNUM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;For each row returned by a query, the &lt;code&gt;ROWNUM&lt;/code&gt; pseudocolumn returns anumber indicating the order in which Oracle selects the row from atable or set of joined rows. The first row selected has a &lt;code&gt;ROWNUM&lt;/code&gt; of&lt;code&gt;1&lt;/code&gt;, the second has &lt;code&gt;2&lt;/code&gt;, and so on. — &lt;a href=&#34;https://docs.oracle.com/en/database/oracle/oracle-database/20/sqlrf/ROWNUM-Pseudocolumn.html#GUID-2E40EC12-3FCF-4A4F-B5F2-6BC669021726&#34;&gt;Oracledocumentation&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;You could do without the subquery, plainly writing:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;firstName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lastName&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;users&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ROWNUM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;but if you add a &lt;code&gt;ORDER BY&lt;/code&gt; clause, &lt;a href=&#34;https://stackoverflow.com/questions/15091849/how-to-use-oracle-order-by-and-rownum-correctly&#34;&gt;Oracle will first select the firstten rows, and only then apply the &lt;code&gt;ORDER BY&lt;/code&gt;&lt;/a&gt;clause, which might not be what you want. So that’s why it’s best toalways use the first idiom above.&lt;/p&gt;&lt;h2 id=&#34;setting-new-password-for-user&#34;&gt;Setting New Password for User&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ALTER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;USER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user_name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IDENTIFIED&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_password&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;show-output-from-script&#34;&gt;Show Output from Script&lt;/h2&gt;&lt;p&gt;When writing a script, you may want to output some diagnostic data:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;BEGIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;EXECUTE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;IMMEDIATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;GRANT INSERT ON my.table TO role&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;EXCEPTION&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OTHERS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;THEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;IF&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SQLCODE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;942&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;THEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DBMS_OUTPUT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PUT_LINE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Table does not exist, buddy&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ELSE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RAISE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ENDIF&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;END&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You think you&amp;rsquo;re good to go, but when you execute your script in an environmentwhere &lt;code&gt;my.table&lt;/code&gt; does not exist, you don&amp;rsquo;t see the diagnostic message. Whatgives? SQL*Plus&amp;rsquo;s default behavior is to suppress output by default. You haveto &lt;code&gt;SET SERVEROUTPUT ON&lt;/code&gt; first.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Mariadb/Mysql Password Exposure in Bash</title>
       <link>https://relentlesscoding.com/posts/mariadb-mysql-password-exposure-in-bash/</link>
       <pubDate>Sun, 16 Feb 2020 19:07:46 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/mariadb-mysql-password-exposure-in-bash/</guid>
       <description>&lt;p&gt;How can we securely provide a password to &lt;code&gt;mysql&lt;/code&gt; without exposing it to theworld by just putting it directly in the command?&lt;/p&gt;&lt;h2 id=&#34;how-to-provide-a-password-to-mysql&#34;&gt;How to Provide a Password to &lt;code&gt;mysql&lt;/code&gt;?&lt;/h2&gt;&lt;p&gt;Recently, I was writing a small Bash program with a GUI (using&lt;a href=&#34;https://help.gnome.org/users/zenity/stable/&#34;&gt;zenity&lt;/a&gt;) that would promptthe user for their MariaDB credentials and then retrieve someinformation. Passing username and password to the &lt;code&gt;mysql&lt;/code&gt; program lookslike:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mysql -u user -h host -ppassword mydatabase -e &amp;#39;select 1;&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(Note that there cannot be a space between &lt;code&gt;-p&lt;/code&gt; and the beginning of thepassword.)&lt;/p&gt;&lt;p&gt;Now, this obviously works fine, except for two problems:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;We cannot reuse the connection to the database, so we have to make anew connection every time we want to execute a query.&lt;/li&gt;&lt;li&gt;The password of user &lt;code&gt;user&lt;/code&gt; is given in the command itself. Thismeans that everyone on the same machine can list the processes andsee the command-line arguments passed to &lt;code&gt;mysql&lt;/code&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;In this post, I want to talk about the second problem.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ pgrep -af mysql8046 mysql -u user -h 192.168.123.5 -px xxxxxxxxxxxxxxxxxxxxxxx mydatabase$ ls -l /proc/8046/cmdline-r--r--r-- 1 neftas neftas 0 16 feb 10:33 /proc/8046/cmdline$ cat -et /proc/8046/cmdlinemysql^@-u^@stefan^@-h^@192.168.123.5^@-px^@xxxxxxxxxxxxxxxxxxxxxxx^@mydatabase^@&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As we can see, &lt;code&gt;mysql&lt;/code&gt; replaces the provided password with &lt;code&gt;x&lt;/code&gt;es, but atthe same time warns in &lt;code&gt;man mysql&lt;/code&gt; that&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Specifying a password on the command line should be consideredinsecure. You can use an option file to avoid giving the password onthe command line.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The option file &lt;code&gt;man mysql&lt;/code&gt; is talking about is either one of theconfiguration files located at &lt;code&gt;$HOME/.my.cnf&lt;/code&gt; or &lt;code&gt;/etc/my.cnf&lt;/code&gt;, or acustom one specified on the command line. So, one option would be to putthe credentials in &lt;code&gt;$HOME/.my.cnf&lt;/code&gt; before executing the program,stopping this issue cold in its tracks.&lt;/p&gt;&lt;p&gt;But let’s say that we want to dynamically connect to a database. Askingthe user to provide credentials and then write them to a file wouldprovide the same issues as passing it directly to the &lt;code&gt;mysql&lt;/code&gt; command.The only difference would be that we now pass the sensitive informationto different command:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;[client]\nhost=%s\nusername=%s\npassword=%s\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;&lt;span class=&#34;go&#34;&gt;    host username password &amp;gt; creds.tmp&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; mysql --defaults-extra-file&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;creds.tmp mydatabase&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;the-environment-solution&#34;&gt;The Environment Solution&lt;/h2&gt;&lt;p&gt;Although not mentioned in my version of &lt;code&gt;man mysql&lt;/code&gt; (10.4 from 29 March2019), the safest way, it turns out, is to set an environment variablenamed &lt;code&gt;MYSQL_PWD&lt;/code&gt;. Why is this safer?&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ls -l /proc/8046/environ-r-------- 1 neftas neftas 0 16 feb 11:00 /proc/8046/environ&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At least on Linux, environment variables are only readable by the userthat started the process. This means other users (beware of &lt;code&gt;root&lt;/code&gt;)cannot look at them.&lt;/p&gt;&lt;p&gt;To summarize:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;declare&lt;/span&gt; -x MYSQL_PWD&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;IFS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;|&amp;#39;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;read&lt;/span&gt; -r username MYSQL_PWD &amp;lt; &amp;lt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;zenity --password --username&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mysql -u &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$username&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; -h host mydatabase&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;declare -x&lt;/code&gt; declares a variable that is going to be exported onassignment. This eliminates the need to &lt;code&gt;export MYSQL_PWD&lt;/code&gt; explicitlysomewhere down the road: when &lt;code&gt;MYSQL_PWD&lt;/code&gt; gets a value assigned, it willexport that value.&lt;/p&gt;&lt;p&gt;In the second line, I use process substitution (see &lt;code&gt;Process Substitution&lt;/code&gt; in &lt;code&gt;man bash&lt;/code&gt;) to provide a pipe-separated string to&lt;code&gt;read&lt;/code&gt; that will split the string and assign the values to &lt;code&gt;username&lt;/code&gt;and &lt;code&gt;MYSQL_PWD&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Now that &lt;code&gt;MYSQL_PWD&lt;/code&gt; is set, we no longer have to worry about providingthe password directly to the &lt;code&gt;mysql&lt;/code&gt; command.&lt;/p&gt;&lt;h2 id=&#34;versions-used-in-this-post&#34;&gt;Versions Used In This Post&lt;/h2&gt;&lt;p&gt;This post was written using the following versions:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ uname -aLinux cirrus7 5.5.3-arch1-1 #1 SMP PREEMPT Tue, 11 Feb 2020 15:35:41 +0000 x86_64 GNU/Linux$ mysql --versionmysql  Ver 15.1 Distrib 10.4.12-MariaDB, for Linux (x86_64) using readline 5.1$ zenity --version3.32.0&lt;/code&gt;&lt;/pre&gt;</description>
     </item>
   
     <item>
       <title>Whats the Difference Between a Login and a Non-Login Shell?</title>
       <link>https://relentlesscoding.com/posts/whats-the-difference-between-a-login-and-a-nonlogin-shell/</link>
       <pubDate>Wed, 23 Jan 2019 20:37:44 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/whats-the-difference-between-a-login-and-a-nonlogin-shell/</guid>
       <description>&lt;p&gt;I always kind of knew there was a difference between login and non-login shells,but when I couldn&amp;rsquo;t explain the difference properly to a coworker, I knew Ineeded to spend some time on figuring it out.&lt;/p&gt;&lt;p&gt;The difference is addressed nicely in the book &lt;em&gt;Unix Power Tools&lt;/em&gt; (Oreilly):&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;When you first log in to a Unix system from a terminal, the systemnormally starts a login shell. A login shell is typically the top-levelshell in the “tree” of processes that starts with the &lt;code&gt;init&lt;/code&gt; process.Many characteristics of processes are passed from parent to childprocess down this “tree” — especially environment variables, such asthe search path. The changes you make in a login shell will affect allthe other processes that the top-level shell starts — including anysubshells.&lt;/p&gt;&lt;p&gt;So, a login shell is where you do general setup that’s done only thefirst time you log in — initialize your terminal, set environmentvariables, and so on. […]&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;So you could think about a login shell as a shell that is started atstartup by the &lt;code&gt;init&lt;/code&gt; process (or &lt;code&gt;systemd&lt;/code&gt; nowadays). Or as a shellthat logs you into the system by your providing a username and apassword. A non-login shell, by contrast, is a shell that is invokedwithout logging anybody in.&lt;/p&gt;&lt;h2 id=&#34;is-my-current-shell-a-login-shell&#34;&gt;Is My Current Shell a Login Shell?&lt;/h2&gt;&lt;p&gt;There are two ways to check if your current shell is a login shell:First, you can check the output of &lt;code&gt;echo $0&lt;/code&gt;: if it starts with a dash(like &lt;code&gt;-bash&lt;/code&gt;), it’s a login shell. Be aware, however, that you canstart a login shell with &lt;code&gt;bash --login&lt;/code&gt;, and &lt;code&gt;echo $0&lt;/code&gt; will output just&lt;code&gt;bash&lt;/code&gt; without the leading dash, so this is not a surefire way of findout if you are running a login shell.&lt;/p&gt;&lt;p&gt;Secondly, the Unix StackOverflow offers &lt;a href=&#34;https://unix.stackexchange.com/questions/26676/how-to-check-if-a-shell-is-login-interactive-batch&#34;&gt;this way of findingout&lt;/a&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ shopt -q login_shell &amp;amp;&amp;amp; echo login || echo nonlogin&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(&lt;code&gt;-q&lt;/code&gt; suppresses the output of the &lt;code&gt;shopt&lt;/code&gt; command.)&lt;/p&gt;&lt;h2 id=&#34;the-difference-between-login-and-non-login-that-actually-matters&#34;&gt;The Difference Between Login and Non-Login That Actually Matters&lt;/h2&gt;&lt;p&gt;Practically speaking, the difference between a login shell and anon-login shell is in the configuration files that Bash reads when itstarts up. In particular, according to &lt;code&gt;man bash&lt;/code&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;[…] it first reads and executes commands from the file&lt;code&gt;/etc/profile&lt;/code&gt;, if that file exists. After reading that file, it looksfor &lt;code&gt;~/.bash_profile&lt;/code&gt;, &lt;code&gt;~/.bash_login&lt;/code&gt;, and &lt;code&gt;~/.profile&lt;/code&gt;, in thatorder, and reads and executes commands from the first one that existsand is readable.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;You can observe this behavior by putting &lt;code&gt;echo&lt;/code&gt; commands in&lt;code&gt;/etc/profile&lt;/code&gt;, &lt;code&gt;~/.bash_profile&lt;/code&gt;, &lt;code&gt;~/.bash_login&lt;/code&gt; and &lt;code&gt;~/.profile&lt;/code&gt;.Upon invoking &lt;code&gt;bash --login&lt;/code&gt; you should see:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;echo from /etc/profileecho from ~/.bash_profile$&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If the shell is a non-login shell, Bash reads and executes commands from&lt;code&gt;~/.bashrc&lt;/code&gt;. Since we are starting a non-login shell from within a loginshell, it will inherit the environment. Sometimes, this will lead toconfusion when we inadvertently get a login shell, and find out that ourconfiguration from &lt;code&gt;~/.bashrc&lt;/code&gt; is not loaded. This is why many peopleput something like the following in their &lt;code&gt;.bash_profile&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[[ -r ~/.bashrc ]] &amp;amp;&amp;amp; source ~/.bashrc&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This test whether &lt;code&gt;.bashrc&lt;/code&gt; is readable and then sources it.&lt;/p&gt;&lt;h2 id=&#34;why-you-sometimes-want-a-login-shell&#34;&gt;Why You Sometimes Want a Login Shell&lt;/h2&gt;&lt;p&gt;When you switch users using &lt;code&gt;su&lt;/code&gt; you will take the environment of thecalling user with you. To prevent this, you should use &lt;code&gt;su -&lt;/code&gt; which isshort for &lt;code&gt;su --login&lt;/code&gt;. This acts like a clean login for a new user, sothe environment will not be cluttered with values from the calling user.Just as before, a login shell will read &lt;code&gt;/etc/profile&lt;/code&gt; and the&lt;code&gt;.bash_profile&lt;/code&gt; of the user you are switching to, but not its &lt;code&gt;.bashrc&lt;/code&gt;.&lt;a href=&#34;https://unix.stackexchange.com/a/7021/73808&#34;&gt;This post onStackOverflow&lt;/a&gt; shows whyyou might want to prefer to start with a clean environment (spoiler:your $PATH might be &amp;ldquo;poisoned&amp;rdquo;).&lt;/p&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;In this article we saw that the main difference between a login and anon-login shell are the configuration files that are read upon startup.We then looked at what the benefits are of a login shell over a non-loginshell.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Encrypt Device With Veracrypt From the Command Line</title>
       <link>https://relentlesscoding.com/posts/encrypt-device-with-veracrypt-from-the-command-line/</link>
       <pubDate>Sun, 06 Jan 2019 21:23:29 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/encrypt-device-with-veracrypt-from-the-command-line/</guid>
       <description>&lt;p&gt;You have a drive that you want to encrypt and use in Linux and otherOSes. Then Veracrypt, the successor of Truecrypt, is a good choice. In thistutorial, I will show you how to quickly encrypt a drive and mount and unmountit from the command line.&lt;/p&gt;&lt;p&gt;The prerequisite for this tutorial is that you already have created a partitionon a drive. See &lt;a href=&#34;https://relentlesscoding.com/posts/partition-and-format-drive-with-ntfs/&#34;&gt;my previous blogpost&lt;/a&gt; on how to accomplish that.Creating a volume on a partition with data on it will &lt;strong&gt;permanently destroy&lt;/strong&gt;that data, so make sure you are encrypting the correct partition (&lt;code&gt;fdisk -l&lt;/code&gt; isyour friend).&lt;/p&gt;&lt;h2 id=&#34;encrypt-a-volume-interactively-from-the-command-line-using-veracrypt&#34;&gt;Encrypt a volume interactively from the command line using Veracrypt…&lt;/h2&gt;&lt;p&gt;(The &lt;code&gt;#&lt;/code&gt; sign at the beginning of the code examples indicates that thecommand should be executed as root. You can either use &lt;code&gt;su -&lt;/code&gt; or &lt;code&gt;sudo&lt;/code&gt;to accomplish this.)&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# veracrypt -t --quick -c /dev/sdXX&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;-t&lt;/code&gt; is short for &lt;code&gt;--text&lt;/code&gt; (meaning you don’t want the GUI) and shouldalways be used first after the command name. The &lt;code&gt;--quick&lt;/code&gt; option &lt;a href=&#34;https://www.veracrypt.fr/en/Creating%20New%20Volumes.html&#34;&gt;isexplained in thedocs&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;If unchecked, each sector of the new volume will be formatted. Thismeans that the new volume will be entirely filled with random data.Quick format is much faster but may be less secure because until thewhole volume has been filled with files, it may be possible to tellhow much data it contains (if the space was not filled with randomdata beforehand). If you are not sure whether to enable or disableQuick Format, we recommend that you leave this option unchecked. Notethat Quick Format can only be enabled when encryptingpartitions/devices.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;So, using &lt;code&gt;--quick&lt;/code&gt; is less secure, but not specifying it could take (alot) longer, especially on traditional hard drives (we’re talking hoursfor 500GB).&lt;/p&gt;&lt;p&gt;Finally, the &lt;code&gt;-c&lt;/code&gt; or &lt;code&gt;--create&lt;/code&gt; command allows us to specify on whichpartition we want to create a VeraCrypt volume. Make sure you change the&lt;code&gt;/dev/sdXX&lt;/code&gt; from the example above to the appropriate output of &lt;code&gt;fdisk -l&lt;/code&gt; (for example, &lt;code&gt;/dev/sdc1&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;This command will interactively guide us to create a new volume:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Volume type: 1) Normal 2) HiddenSelect [1]: 1Encryption Algorithm: 1) AES 2) Serpent 3) Twofish 4) Camellia 5) Kuznyechik 6) AES(Twofish) 7) AES(Twofish(Serpent)) 8) Camellia(Kuznyechik) 9) Camellia(Serpent) 10) Kuznyechik(AES) 11) Kuznyechik(Serpent(Camellia)) 12) Kuznyechik(Twofish) 13) Serpent(AES) 14) Serpent(Twofish(AES)) 15) Twofish(Serpent)Select [1]: 1Hash algorithm: 1) SHA-512 2) Whirlpool 3) SHA-256 4) StreebogSelect [1]: 1Filesystem: 1) None 2) FAT 3) Linux Ext2 4) Linux Ext3 5) Linux Ext4 6) NTFS 7) exFATSelect [2]: 6Enter password:WARNING: Short passwords are easy to crack using brute force techniques!We recommend choosing a password consisting of 20 or more characters. Are you sure you want to use a short password? (y=Yes/n=No) [No]: yRe-enter password:Enter PIM:Enter keyfile path [none]:Please type at least 320 randomly chosen characters and then press Enter:Characters remaining: 4Done: 100.000%  Speed: 61.8 GB/s  Left: 0 sThe VeraCrypt volume has been successfully created.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The volume is now created in the partition and is ready to be mounted.&lt;/p&gt;&lt;h2 id=&#34;-or-do-it-all-in-a-one-liner&#34;&gt;… Or do it all in a one-liner&lt;/h2&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# veracrypt --text --quick                      \        --non-interactive                       \        --create /dev/sdXX                      \        --volume-type=normal                    \        --encryption=AES                        \        --hash=SHA-512                          \        --filesystem=NTFS                       \        --password=&amp;#39;Un$@f3&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Use &lt;code&gt;--stdin&lt;/code&gt; to read the password from the standard in, instead ofsupplying it directly to the command, which is considered unsecure.&lt;/p&gt;&lt;h2 id=&#34;mounting-the-volume&#34;&gt;Mounting the volume&lt;/h2&gt;&lt;p&gt;Linux:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# mkdir /tmp/vera# veracrypt -t /dev/sdXX /tmp/vera&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Windows:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;:: first find the VolumeName of the partition&amp;gt; mountvol.exe... snip ...Possible values for VolumeName along with current mount points are:    \\?\Volume{3676a1ae-0000-0000-0000-100000000000}\        *** NO MOUNT POINTS ***    \\?\Volume{1b98f0ba-8bc1-b740-b21f-f570bf2367dd}\        E:\    \\?\Volume{3676a1ae-0000-0000-0000-300300000000}\        C:\    \\?\Volume{3676a1ae-0000-0000-0000-c0a01f000000}\        *** NOT MOUNTABLE UNTIL A VOLUME MOUNT POINT IS CREATED ***    \\?\Volume{813379b4-3e59-11eb-bbcd-806e6f6e6963}\        D:\New volumes are not mounted automatically when added to the system.  To mount avolume, you must create a volume mount point.&amp;gt;&amp;gt;:: in my case, the VeraCrypt partition is mounted at E:&amp;gt;:: I&amp;#39;ll make it available decrypted at Z:&amp;gt;&amp;gt;VeraCrypt.exe /v \\?\Volume{1b98f0ba-8bc1-b740-b21f-f570bf2367dd}\ /l z /q&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point, a dialog shows where you can enter you password. I wouldn&amp;rsquo;trecommend it, but you can also specify &lt;code&gt;/p &amp;lt;password&amp;gt;&lt;/code&gt; or &lt;code&gt;/password &amp;lt;password&amp;gt;&lt;/code&gt; on the command line and skip the dialog.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;/q&lt;/code&gt; or &lt;code&gt;/quit&lt;/code&gt; option makes sure the main VeraCrypt window is notdisplayed.&lt;/p&gt;&lt;p&gt;Of course, using the GUI makes all of this even simpler, as you don&amp;rsquo;t have tobother with finding the &lt;code&gt;VolumeName&lt;/code&gt; yourself.&lt;/p&gt;&lt;h2 id=&#34;unmounting-the-volume&#34;&gt;Unmounting the volume&lt;/h2&gt;&lt;p&gt;Linux:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# veracrypt -d /tmp/vera&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Windows:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;VeraCrypt.exe /d Z:&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;more-info&#34;&gt;More info&lt;/h2&gt;&lt;p&gt;Linux:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ veracrypt -t -h&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;-h&lt;/code&gt; is short for &lt;code&gt;--help&lt;/code&gt; and should be self-explanatory.&lt;/p&gt;&lt;p&gt;Windows:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;VeraCrypt.exe /help&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or &lt;a href=&#34;https://www.veracrypt.fr/code/VeraCrypt/plain/doc/html/Command%20Line%20Usage.html&#34;&gt;read about the command-line options for Windows online&lt;/a&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Make Less Options Permanent or the Missing Lessrc</title>
       <link>https://relentlesscoding.com/posts/make-less-options-permanent-or-the-missing-lessrc/</link>
       <pubDate>Sun, 06 Jan 2019 20:15:50 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/make-less-options-permanent-or-the-missing-lessrc/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s have a look at how we can make certain &lt;code&gt;less&lt;/code&gt; preferences permanent,like &lt;code&gt;-I&lt;/code&gt;, for example, which will make search case insensitive.&lt;/p&gt;&lt;h2 id=&#34;the-missing-homelessrc&#34;&gt;The missing &lt;code&gt;$HOME/.lessrc&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;In GNU/Linux, preferences are often stored in &lt;code&gt;rc&lt;/code&gt; files. For &lt;code&gt;vim&lt;/code&gt; we have&lt;code&gt;.vimrc&lt;/code&gt;, for Bash &lt;code&gt;.bashrc&lt;/code&gt;, etc:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ find &amp;#34;$HOME&amp;#34; -maxdepth 1 -name &amp;#39;*rc&amp;#39;./.vimrc./.idlerc./.xinitrc./.lynxrc./.old_netrc./.inputrc./.bashrc./.rtorrent.rc./.sqliterc./.xdvirc&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;environment-variable-less&#34;&gt;Environment variable &lt;code&gt;LESS&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;So, it would make sense to expect a &lt;code&gt;.lessrc&lt;/code&gt;. But there is none.Instead, we define a environment variable &lt;code&gt;LESS&lt;/code&gt;. My &lt;code&gt;.bashrc&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;export LESS=&amp;#34;IFRSX&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Breakdown:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;-I&lt;/code&gt;: ignore case when searching&lt;/li&gt;&lt;li&gt;&lt;code&gt;-F&lt;/code&gt;: quit immediately when the entire file fits in one screen (ineffect, mimic &lt;code&gt;cat&lt;/code&gt;’s behavior)&lt;/li&gt;&lt;li&gt;&lt;code&gt;-R&lt;/code&gt;: enable colored output (for example, when piping to &lt;code&gt;less&lt;/code&gt; from&lt;code&gt;diff --color=always&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;&lt;code&gt;-S&lt;/code&gt;: truncate long lines instead of wrapping them to the next line&lt;/li&gt;&lt;li&gt;&lt;code&gt;-X&lt;/code&gt;: don’t clear screen on exit&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;See &lt;a href=&#34;https://linux.die.net/man/1/less&#34;&gt;&lt;code&gt;man 1 less&lt;/code&gt;&lt;/a&gt; for all options.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Make a Backup With Rsync</title>
       <link>https://relentlesscoding.com/posts/make-a-backup-with-rsync/</link>
       <pubDate>Sun, 06 Jan 2019 17:21:58 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/make-a-backup-with-rsync/</guid>
       <description>&lt;p&gt;We want to make a backup of data, for example to an external hard drive. Rsyncto the rescue.&lt;/p&gt;&lt;h2 id=&#34;the-basic-command&#34;&gt;The basic command&lt;/h2&gt;&lt;p&gt;Assuming we are in someone’s home directory and we want to copy threesource directories &lt;code&gt;Music&lt;/code&gt;, &lt;code&gt;Documents&lt;/code&gt; and &lt;code&gt;Movies&lt;/code&gt; to a destinationdirectory &lt;code&gt;/mnt/external-hdd&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ rsync -a Music Documents Movies /mnt/external-hdd&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;a-word-on-slashes&#34;&gt;A word on slashes&lt;/h2&gt;&lt;p&gt;Notice that we omit the trailing forward slash &lt;code&gt;/&lt;/code&gt; on the sourcedirectories. This means the destination will look like:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/mnt/external-hdd|-- Music|   |-- a.mp3|-- Documents|   |-- b.txt|-- Movies|   |-- c.mp4&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we were to add trailing forward slashes, the upper-level sourcedirectories would not be copied and the result would look like:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/mnt/external-hdd|-- a.mp3|-- b.txt|-- c.mp4&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;http://qdosmsq.dunbar-it.co.uk/blog/2013/02/rsync-to-slash-or-not-to-slash/&#34;&gt;Read more about slashes in&lt;code&gt;rsync&lt;/code&gt;.&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&#34;the-rsync--a-command-broken-down&#34;&gt;The &lt;code&gt;rsync -a&lt;/code&gt; command broken down&lt;/h2&gt;&lt;p&gt;&lt;code&gt;rsync -a&lt;/code&gt; is equal to &lt;code&gt;rsync --archive&lt;/code&gt; and is a convenience command.According to the &lt;a href=&#34;&#34;&gt;man page&lt;/a&gt;, it equals &lt;code&gt;rsync -rlptgoD&lt;/code&gt;.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;-r&lt;/code&gt; or &lt;code&gt;--recurse&lt;/code&gt;: recursively copy data&lt;/li&gt;&lt;li&gt;&lt;code&gt;-l&lt;/code&gt; or &lt;code&gt;--links&lt;/code&gt;: copy symlinks as symlinks&lt;/li&gt;&lt;li&gt;&lt;code&gt;-p&lt;/code&gt; or &lt;code&gt;--perms&lt;/code&gt;: preserve permissions&lt;/li&gt;&lt;li&gt;&lt;code&gt;-t&lt;/code&gt; or &lt;code&gt;--times&lt;/code&gt;: preserve modifications times&lt;/li&gt;&lt;li&gt;&lt;code&gt;-g&lt;/code&gt; or &lt;code&gt;--group&lt;/code&gt;: preserve the group&lt;/li&gt;&lt;li&gt;&lt;code&gt;-o&lt;/code&gt; or &lt;code&gt;--owner&lt;/code&gt;: preserve owner&lt;/li&gt;&lt;li&gt;&lt;code&gt;-D&lt;/code&gt; is the same as &lt;code&gt;--devices --specials&lt;/code&gt;:&lt;/li&gt;&lt;li&gt;&lt;code&gt;--devices&lt;/code&gt;: preserve device files&lt;/li&gt;&lt;li&gt;&lt;code&gt;--specials&lt;/code&gt;: preserve special files&lt;/li&gt;&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;[…] a &lt;strong&gt;device file&lt;/strong&gt; or &lt;strong&gt;special file&lt;/strong&gt; is an interface to adevice driver that appears in a file system as if it were an ordinaryfile&lt;/p&gt;&lt;p&gt;— &lt;a href=&#34;https://en.wikipedia.org/wiki/Device_file&#34;&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Device files in Linux are usually found under the &lt;code&gt;/dev&lt;/code&gt; directory.&lt;/p&gt;&lt;h2 id=&#34;see-the-overall-progress-of-rsync&#34;&gt;See the overall progress of &lt;code&gt;rsync&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;By default, &lt;code&gt;rsync&lt;/code&gt; will show the progress of the individual files thatare being copied. If you want the overall progress, you have to add someflags:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ rsync -a --info=progress2 --no-i-r src dst&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;--info=progress2&lt;/code&gt; shows the total transfer progress. (To see allavailable options for &lt;code&gt;--info&lt;/code&gt;, execute &lt;code&gt;rsync --info=help&lt;/code&gt;). &lt;code&gt;--no-i-r&lt;/code&gt;is short for &lt;code&gt;--no-inc-recursive&lt;/code&gt; and disables incremental recursion,forcing &lt;code&gt;rsync&lt;/code&gt; to do a complete scan of of all directories beforestarting the file transfer. This is needed to get an accurate progressreport, otherwise &lt;code&gt;rsync&lt;/code&gt; doesn’t know how much work is left.&lt;/p&gt;&lt;p&gt;Human-readable output can be obtained by passing the &lt;code&gt;-h&lt;/code&gt; or&lt;code&gt;--human-readable&lt;/code&gt; option.&lt;/p&gt;&lt;p&gt;For a discussion of these options, see also &lt;a href=&#34;https://serverfault.com/questions/219013/showing-total-progress-in-rsync-is-it-possible&#34;&gt;this StackOverflowpost&lt;/a&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Partition and Format Drive With NTFS</title>
       <link>https://relentlesscoding.com/posts/partition-and-format-drive-with-ntfs/</link>
       <pubDate>Sat, 05 Jan 2019 20:11:43 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/partition-and-format-drive-with-ntfs/</guid>
       <description>&lt;p&gt;Say we bought an external hard drive to back up some stuff from acrashed computer. We can use a Live USB to get at the data and put thedata on the external hard drive. Because the data needs to be accessibleby Windows, we are going to use format the drive with NTFS.&lt;/p&gt;&lt;h2 id=&#34;create-partition&#34;&gt;Create partition&lt;/h2&gt;&lt;p&gt;Connect the external hard disk to your computer. Use &lt;code&gt;sudo fdisk -l&lt;/code&gt; tofind the device name. Output should look something like this:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Disk /dev/sdb: 1.8 TiB, 2000398934016 bytes, 3907029168 sectorsDisk model: CT2000MX500SSD1Units: sectors of 1 * 512 = 512 bytesSector size (logical/physical): 512 bytes / 4096 bytesI/O size (minimum/optimal): 4096 bytes / 4096 bytesDisklabel type: dosDisk identifier: 0x117d68c1Device     Boot Start        End    Sectors  Size Id Type/dev/sdb1        2048 3907029167 3907027120  1.8T 83 Linux&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As can been seen above, the name of the device is &lt;code&gt;/dev/sdb&lt;/code&gt;. We use toname to run &lt;code&gt;fdisk&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sudo fdisk /dev/sdb&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice how we use the name of the device, and not the name of thepartition (so &lt;code&gt;/dev/sdb&lt;/code&gt; without any numbers attached at the end).&lt;/p&gt;&lt;p&gt;After entering the command above, an interactive menu will be facingyou. Type a letter and press &lt;span class=&#34;kbd&#34;&gt;Enter&lt;/span&gt; to confirm.Changes will only be applied when you type &lt;code&gt;w&lt;/code&gt;, so if you make amistake, just stay calm and press &lt;code&gt;q&lt;/code&gt; and you will exit &lt;code&gt;fdisk&lt;/code&gt; withyour pending changes discarded.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Delete all your existing partitions by pressing &lt;code&gt;d&lt;/code&gt;. Depending onthe amount of partitions, you might have to repeat this severaltimes. If you want to check the current partition table, press &lt;code&gt;p&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;After all old partitions are deleted, add a new partition bypressing &lt;code&gt;n&lt;/code&gt;. If you just want to create a single partition on yourdrive, accept all the defaults by pressing&lt;span class=&#34;kbd&#34;&gt;Enter&lt;/span&gt; on each prompt. This will leave youwith a single partition that will take up all space on the drive.&lt;/li&gt;&lt;li&gt;Back in the main menu, type &lt;code&gt;t&lt;/code&gt; to change the partition type. Press&lt;code&gt;L&lt;/code&gt; to see all partitions types. Here we are going to choose &lt;code&gt;7&lt;/code&gt;(&lt;code&gt;HPFS/NTFS/exFAT&lt;/code&gt;). “The partition type […] is a byte valueintended to specify the file system the partition contains and/or toflag special access methods used to access these partitions”(&lt;a href=&#34;https://en.wikipedia.org/wiki/Partition_type&#34;&gt;source&lt;/a&gt;). &lt;a href=&#34;https://www.linuxquestions.org/questions/linux-software-2/difference-between-partition-type-and-file-system-4175597151/&#34;&gt;Linuxdoes not care about the partitiontype&lt;/a&gt;,but Windows does, so we have to change it.&lt;/li&gt;&lt;li&gt;Press &lt;code&gt;w&lt;/code&gt; to write your changes to the disk and exit &lt;code&gt;fdisk&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;format-partition-with-ntfs&#34;&gt;Format partition with NTFS&lt;/h2&gt;&lt;p&gt;Now we create the actual NTFS file system on the drive:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sudo mkfs.ntfs -Q -L label /dev/sdX1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(If you don’t have &lt;code&gt;mkfs.ntfs&lt;/code&gt; installed, use your distro’s packagemanager to install it (on Arch Linux it’s in a package called&lt;code&gt;ntfs-3g&lt;/code&gt;)).&lt;/p&gt;&lt;p&gt;Breakdown:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;-Q&lt;/code&gt; is the same as &lt;code&gt;--quick&lt;/code&gt;, &lt;code&gt;-f&lt;/code&gt; or &lt;code&gt;--fast&lt;/code&gt;. This will perfom aquick format, meaning that it will skip both zeroing of the volumeand and bad sector checking. So obviously, leave this option out ifyou want the volume to be zeroed or you want error checking.Depending on the size of your partition, this might take quite awhile.&lt;/li&gt;&lt;li&gt;&lt;code&gt;-L&lt;/code&gt; is the same as &lt;code&gt;--label&lt;/code&gt;: it’s the identifier you’ll see inWindows Explorer when your drive is connected.&lt;/li&gt;&lt;li&gt;&lt;code&gt;dev/sdX1&lt;/code&gt;: change the &lt;code&gt;X&lt;/code&gt; to the actual letter of your drive wefound earlier in this tutorial. You always format a partition, not adrive, so make sure that you put the correct number of the partitionyou want formatted at the end.&lt;/li&gt;&lt;/ul&gt;</description>
     </item>
   
     <item>
       <title>Create Arch Linux Live USB</title>
       <link>https://relentlesscoding.com/posts/create-arch-linux-live-usb/</link>
       <pubDate>Sat, 05 Jan 2019 17:31:05 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/create-arch-linux-live-usb/</guid>
       <description>&lt;p&gt;Creating a Arch Linux live USB is easy. In this post, I will walk you throughdownloading and verifying the image, finding your USB drive and copying theimage onto it, all from the convenience of the command line.&lt;/p&gt;&lt;h3 id=&#34;download-image-and-pgp-signature&#34;&gt;Download image and PGP signature&lt;/h3&gt;&lt;p&gt;Download the latest Arch Linux image from &lt;a href=&#34;https://www.archlinux.org/download/&#34;&gt;https://www.archlinux.org/download/&lt;/a&gt;.The preferred option is to download the image using BitTorrent, in order not toburden the Arch servers unnecessarily.&lt;/p&gt;&lt;h3 id=&#34;verify-downloaded-image&#34;&gt;Verify downloaded image&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ gpg --keyserver pgp.mit.edu                       \        --keyserver-options auto-key-retrieve       \        --verify archlinux-version-x86_64.iso.sig&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If this disk is being created on an Arch Linux system, you could alsoinvoke:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ pacman-key -v archlinux-version-x86_64.iso.sig&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;-v&lt;/code&gt; switch is short for &lt;code&gt;--verify&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&#34;insert-usb-drive-and-check-the-device-name&#34;&gt;Insert USB drive and check the device name&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sudo fdisk -l&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;-l&lt;/code&gt; is short for &lt;code&gt;--list&lt;/code&gt;, and will display the device names andpartition tables.&lt;/p&gt;&lt;p&gt;Output will look like:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Disk /dev/sdb: 1.8 TiB, 2000398934016 bytes, 3907029168 sectorsDisk model: CT2000MX500SSD1Units: sectors of 1 * 512 = 512 bytesSector size (logical/physical): 512 bytes / 4096 bytesI/O size (minimum/optimal): 4096 bytes / 4096 bytesDisklabel type: dosDisk identifier: 0x117d68c1Device     Boot Start        End    Sectors  Size Id Type/dev/sdb1        2048 3907029167 3907027120  1.8T 83 Linux&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Check the output and find the device name of the USB (for instance&lt;code&gt;/dev/sdc&lt;/code&gt;). Make sure this device is &lt;strong&gt;not mounted&lt;/strong&gt;, otherwise thenext command will fail. Also make sure you note the device name, and&lt;em&gt;not&lt;/em&gt; a partition (indicated by a numeral at the end: &lt;code&gt;/dev/sdc1&lt;/code&gt;, forexample).&lt;/p&gt;&lt;h3 id=&#34;copy-arch-linux-image-to-usb-drive&#34;&gt;Copy Arch Linux image to USB drive&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sudo dd if=archlinux-2019-01-01-x86_64.iso    \        of=/dev/sdX                             \        bs=64K                                  \        oflag=sync                              \        status=progess&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Breakdown:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;if&lt;/code&gt; indicates the input file (the &lt;code&gt;.iso&lt;/code&gt; of the live Linux distro).&lt;/li&gt;&lt;li&gt;&lt;code&gt;of&lt;/code&gt;, likewise, points to the output file, which is a device in thiscase. Note that &lt;code&gt;/dev/sdX&lt;/code&gt; needs to be replaced with the device namewe found in the previous step.&lt;/li&gt;&lt;li&gt;&lt;code&gt;bs=64K&lt;/code&gt; indicates the block size, which means that &lt;code&gt;dd&lt;/code&gt; will readand write up to 64K bytes at a time. The default is 512 bytes. Itreally depends what the optimal block size is, but &lt;a href=&#34;https://stackoverflow.com/questions/6161823/dd-how-to-calculate-optimal-blocksize&#34;&gt;severalsources&lt;/a&gt;indicate that 64K is a good bet on somewhat modern to modernhardware.&lt;/li&gt;&lt;li&gt;&lt;code&gt;oflag&lt;/code&gt; stands for “output flag”. The &lt;code&gt;sync&lt;/code&gt; flag will make surethat all data is written to the USB stick when the &lt;code&gt;dd&lt;/code&gt; commandexits, so it will be safe to remove the USB stick.&lt;/li&gt;&lt;li&gt;&lt;code&gt;status=progress&lt;/code&gt; indicates the level of information that is printedduring file transfer. &lt;code&gt;progress&lt;/code&gt; shows periodic transfer statistics.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Notice that the device does not need to be partitioned or empty beforethis operation. When &lt;code&gt;dd&lt;/code&gt; writes to a device rather than a partition,all data on the drive – including partitions – will be erased anyway.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Spring Basics Conditional Bean Wiring</title>
       <link>https://relentlesscoding.com/posts/spring-basics-conditional-bean-wiring/</link>
       <pubDate>Fri, 19 Oct 2018 19:18:16 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/spring-basics-conditional-bean-wiring/</guid>
       <description>&lt;p&gt;In Spring, it&amp;rsquo;s possible to wire a bean only when some condition is met.&lt;/p&gt;&lt;h2 id=&#34;a-use-case-for-conditional-bean-wiring&#34;&gt;A Use Case for Conditional Bean Wiring&lt;/h2&gt;&lt;p&gt;Suppose we want to enable caching for our web application. We areinterested to find out whether this helps our application or whether theoverhead will so big that it actually slows our application down. Wedecide we want to put the new functionality behind a feature toggle. Weadd a new property to our application called&lt;code&gt;app.caching.enabled=false&lt;/code&gt;. When we are ready to enable the caching, wechange the property’s value to &lt;code&gt;true&lt;/code&gt; and we are in business. When theresults disappoint, we can easily revert the property’s value to&lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;conditional-introduction&#34;&gt;&lt;code&gt;@Conditional&lt;/code&gt; Introduction&lt;/h2&gt;&lt;p&gt;The &lt;code&gt;@Conditional&lt;/code&gt; annotation can be used on any type or method thatdeclares a bean:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Conditional&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SomeCondition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Bean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SomeBean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;someBean&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SomeBean&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Conditional.html&#34;&gt;&lt;code&gt;@Conditional&lt;/code&gt;&lt;/a&gt;takes a mandatory class that implements the functional interface&lt;a href=&#34;https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Condition.html&#34;&gt;&lt;code&gt;Condition&lt;/code&gt;&lt;/a&gt;.&lt;code&gt;Condition&lt;/code&gt; defines a single method &lt;code&gt;matches&lt;/code&gt; that returns a boolean.The method decides whether the bean should be loaded into the Springcontext or whether it should be ignored:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SomeCondition&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Condition&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;matches&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ConditionContext&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AnnotatedTypeMetadata&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;metadata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;cm&#34;&gt;/* make decision based on context and metadata */&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;a-conditional-configuration&#34;&gt;A &lt;code&gt;@Conditional&lt;/code&gt; &lt;code&gt;@Configuration&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;When &lt;code&gt;@Conditional&lt;/code&gt; is applied to a class which is also annotated with&lt;code&gt;@Configuration&lt;/code&gt;, then all of the &lt;code&gt;@Bean&lt;/code&gt; methods, &lt;code&gt;@Import&lt;/code&gt;,&lt;code&gt;@ComponentScan&lt;/code&gt; and other annotations will be subject to the condition.This means that when the condition evaluates to &lt;code&gt;false&lt;/code&gt;, all of theconfiguration defined in that class will be ignored.&lt;/p&gt;&lt;p&gt;Since creating a cache carries costs with it (even an unused cachereserves space on the heap for its initial size), this is exactly whatwe want in our situation.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Configuration&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Conditional&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CacheCondition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;CacheConfig&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;cm&#34;&gt;/* ... define cache manager and caches here ... */&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CacheCondition&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ConfigurationCondition&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ConfigurationPhase&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getConfigurationPhase&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ConfigurationPhase&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;PARSE_CONFIGURATION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;matches&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ConditionContext&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AnnotatedTypeMetadata&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;metadata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getEnvironment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getRequiredProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;app.caching.enabled&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Boolean&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since we are dealing with a conditional configuration instead of aregular bean, the condition class implements&lt;a href=&#34;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/ConfigurationCondition.html&#34;&gt;&lt;code&gt;ConfigurationCondition&lt;/code&gt;&lt;/a&gt;instead of &lt;code&gt;Condition&lt;/code&gt;. The &lt;code&gt;ConfigurationCondition&lt;/code&gt; makes us implementanother method that specifies at which point the condition should beevaluated. Normally, this&lt;a href=&#34;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/ConfigurationCondition.html&#34;&gt;&lt;code&gt;ConfigurationPhase&lt;/code&gt;&lt;/a&gt;is set to &lt;code&gt;REGISTER_BEAN&lt;/code&gt;, which means it checks the condition whenadding a regular bean. In this case, we want it set to&lt;code&gt;PARSE_CONFIGURATION&lt;/code&gt;, which makes Spring evaluate the condition at themoment the configuration class is parsed. If the condition does notmatch at that moment, the &lt;code&gt;@Configuration&lt;/code&gt; class will not be added tothe context.&lt;/p&gt;&lt;h2 id=&#34;dependencies&#34;&gt;Dependencies&lt;/h2&gt;&lt;p&gt;The only dependency is on &lt;code&gt;spring-context&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-context&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.1.1.RELEASE&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;sample-project&#34;&gt;Sample Project&lt;/h2&gt;&lt;p&gt;A sample project can be found &lt;a href=&#34;https://gitlab.com/neftas/spring-basics-conditional-annotation&#34;&gt;onGitLab&lt;/a&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Spring Basics Dynamically Inject Values With Springs Value</title>
       <link>https://relentlesscoding.com/posts/spring-basics-dynamically-inject-values-with-springs-value/</link>
       <pubDate>Sun, 09 Sep 2018 18:57:22 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/spring-basics-dynamically-inject-values-with-springs-value/</guid>
       <description>&lt;p&gt;If we do not want to hard-code values into our source code, we can useproperties files. With the &lt;code&gt;@Value&lt;/code&gt; annotation, Spring gives us an easymeans to get properties from properties files and inject them into ourcode.&lt;/p&gt;&lt;h2 id=&#34;dependencies&#34;&gt;Dependencies&lt;/h2&gt;&lt;p&gt;This post was written with Spring 5.0.5.RELEASE and Java 1.8.0_181.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-context&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.0.5.RELEASE&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;inject-scalars&#34;&gt;Inject Scalars&lt;/h2&gt;&lt;p&gt;Let’s say we have the following key-value pairs in a file&lt;code&gt;app.properties&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;app.string.property&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;hello&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;app.integer.property&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;987&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;app.floating.point.property&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;3.14159&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;app.boolean.property&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To get access to these properties, we declare an applicationconfiguration in Java:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Configuration&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@ComponentScan&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@PropertySource&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;app.properties&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;AppConfig&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;@PropertySource&lt;/code&gt; adds a property source to Spring’s &lt;code&gt;Environment&lt;/code&gt;.Here, the properties file is placed in the root of the classpath (andsince I am using Maven default paths, that would be&lt;code&gt;src/main/resources&lt;/code&gt;.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;com.relentlesscoding.wirebeans&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.junit.Assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.junit.Test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.junit.runner.RunWith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.beans.factory.annotation.Value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.test.context.ContextConfiguration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.test.context.junit4.SpringRunner&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.hamcrest.CoreMatchers.is&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.junit.Assert.assertThat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@RunWith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SpringRunner&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@ContextConfiguration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;classes&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AppConfig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;HabitTest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;${app.string.property}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stringProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;${app.integer.property}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;integerProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;${app.floating.point.property}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;floatingPointProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;${app.boolean.property}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;booleanProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;stringProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertThat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stringProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;integerProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertThat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;integerProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;987&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;floatingPointProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertThat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;floatingPointProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;14159F&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;booleanProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(){&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertThat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;booleanProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;injecting-complex-values&#34;&gt;Injecting Complex Values&lt;/h2&gt;&lt;p&gt;Let’s say we also have collections of values in our properties file:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;app.collection.strings.property&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;one, two, three&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;app.collection.floats.property&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;1.2, 3.4, 5.6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;app.collection.dates.property&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;2018-09-30T10:00:00, 2018-09-29T11:00:00, 2018-09-28T12:00:00&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default, Spring won’t be able to interpret &lt;code&gt;List&lt;/code&gt;s of values. Thebest we can do, by default, is getting an &lt;em&gt;array&lt;/em&gt; of &lt;code&gt;String&lt;/code&gt;s:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;${app.collection.strings.property}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stringArrayProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;stringsArrayProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertArrayEquals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;one&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;two&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;three&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stringArrayProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;declare-a-conversionservice-to-inject-collections-and-other-complex-types&#34;&gt;Declare a &lt;code&gt;ConversionService&lt;/code&gt; to Inject Collections And Other Complex Types&lt;/h3&gt;&lt;p&gt;To convert properties to other types than strings, we need to enableSpring’s&lt;a href=&#34;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/convert/ConversionService.html&#34;&gt;&lt;code&gt;ConversionService&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Bean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ConversionService&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;conversionService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DefaultFormattingConversionService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We instantiate &lt;code&gt;DefaultFormattingConversionService&lt;/code&gt;, which is“configured by default with converters and formatters appropriate formost applications”. That means that it can convert comma-separatedstrings to common, generic collection types, and can convert strings todates, currencies and &lt;code&gt;TimeZone&lt;/code&gt;s, for example.&lt;/p&gt;&lt;p&gt;Notice that the method is declared &lt;code&gt;static&lt;/code&gt;. &lt;code&gt;ConversionService&lt;/code&gt; is oftype &lt;code&gt;BeanFactoryPostProcessor&lt;/code&gt; and must be instantiated very early inthe Spring container life cycle, so that its services are available whenprocessing of annotations such as &lt;code&gt;@Autowired&lt;/code&gt; and &lt;code&gt;@Value&lt;/code&gt; are done. Bydeclaring the method &lt;code&gt;static&lt;/code&gt;, the &lt;code&gt;ConversionService&lt;/code&gt; can be invokedwithout instantiating the enclosing &lt;code&gt;@Configuration&lt;/code&gt; class. Therefore,we can use &lt;code&gt;@Value&lt;/code&gt; (and other annotations) that make use of thisconverter in &lt;em&gt;the same class&lt;/em&gt;, without running into the trouble that&lt;code&gt;ConversionService&lt;/code&gt; is not yet instantiated.&lt;/p&gt;&lt;p&gt;To learn more about bean instantiation, read the part under&lt;a href=&#34;https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html&#34;&gt;&lt;code&gt;BeanFactoryPostProcessor&lt;/code&gt;-returning &lt;code&gt;@Bean&lt;/code&gt;methods&lt;/a&gt;.Also, &lt;a href=&#34;https://stackoverflow.com/a/31766007/2521769&#34;&gt;this answer onStackOverflow&lt;/a&gt; is very clarifying.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;floatArrayProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertArrayEquals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;2F&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;4F&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;6F&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;floatArrayProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;01F&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To convert dates from a properties string to actual date objects we needan extra step: &lt;code&gt;@DateTimeFormat&lt;/code&gt;. This annotation indicates the formatof the date to Spring. In this case, we format our property strings toconform to ISO-8601 or &lt;code&gt;DateTimeFormat.ISO.DATE_TIME&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@DateTimeFormat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;iso&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ISO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;DATE_TIME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;${app.collection.dates.property}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalDate&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listOfDatesProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;listOfDatesProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertThat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listOfDatesProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertEquals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalDate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;2018-09-30T10:00:00&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DateTimeFormatter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ISO_DATE_TIME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listOfDatesProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Thanks to our &lt;code&gt;ConversionService&lt;/code&gt;, we can now convert not only toarrays, but also to collections.&lt;/p&gt;&lt;h3 id=&#34;inject-maps-with-springs-value&#34;&gt;Inject Maps With Spring’s &lt;span class=&#34;citation&#34; data-cites=&#34;Value&#34;&gt;@Value&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;If we throw in a Spring Expression Language (SpEL) expression, we caneven have dictionaries in our properties and convert them to &lt;code&gt;Map&lt;/code&gt;s:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;app.collection.map.string.to.integer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;{one:&amp;#34;1&amp;#34;, two:&amp;#34;2&amp;#34;, three:&amp;#34;3&amp;#34;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;#{${app.collection.map.string.to.integer}}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Map&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Integer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mapStringToInteger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;mapProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertThat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mapStringToInteger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertEquals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Integer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mapStringToInteger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;one&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertEquals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Integer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mapStringToInteger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;two&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertEquals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Integer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mapStringToInteger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;three&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice the &lt;code&gt;#{...}&lt;/code&gt; that delimits the SpEL expression. It evaluates thestring that comes from the properties files and parses it to a &lt;code&gt;Map&lt;/code&gt;. Tounderstand how this works, let’s have a look at what a literal map in aSpEL expression would look like:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;#{{1: &amp;#39;Catch-22&amp;#39;, 2: &amp;#39;1984&amp;#39;, 3: &amp;#39;Pride and Prejudice&amp;#39;}}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Map&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Integer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;books&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A literal map in a SpEL expression is delimited by braces &lt;code&gt;{key: &#39;value&#39;, ...}&lt;/code&gt;. This is exactly what we had in our properties file.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Spring Basics XML Setter Injection With Custom Method Names</title>
       <link>https://relentlesscoding.com/posts/spring-basics-xml-setter-injection-with-custom-method-names/</link>
       <pubDate>Sun, 09 Sep 2018 11:46:25 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/spring-basics-xml-setter-injection-with-custom-method-names/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s have a look at how to inject beans using setters with a custom methodname.&lt;/p&gt;&lt;p&gt;In a &lt;a href=&#34;https://relentlesscoding.com/posts/spring-basics-wiring-beans-with-xml-configuration/&#34;&gt;previous blogpost&lt;/a&gt; we ended upwith the following XML-based configuration:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34; ?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;beans&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;xmlns=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/beans&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xmlns:util=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/util&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/beans&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;       http://www.springframework.org/schema/beans/spring-beans.xsd&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;       http://www.springframework.org/schema/util&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;       http://www.springframework.org/schema/util/spring-util.xsd&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;startTime&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;java.time.LocalDate&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;factory-method=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;now&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;firstStreak&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.PositiveStreak&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;constructor-arg&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;startTime&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;secondStreak&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.PositiveStreak&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;constructor-arg&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;startTime&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;util:list&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;myRunningStreaks&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;ref&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;bean=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;firstStreak&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;ref&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;bean=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;secondStreak&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/util:list&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;running&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.Running&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;streaks&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;myRunningStreaks&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/beans&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The complete code can be found &lt;a href=&#34;https://gitlab.com/neftas/wire-spring-beans-xml&#34;&gt;onGitLab&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;To recap, we instantiate a bean of type &lt;code&gt;LocalDate&lt;/code&gt; by invoking its&lt;code&gt;now()&lt;/code&gt; static method. We inject this bean into two &lt;code&gt;PositiveStreak&lt;/code&gt;beans using constructor injection. We reference the &lt;code&gt;LocalDate&lt;/code&gt; bean byits id, &lt;code&gt;startTime&lt;/code&gt;. Next, we create a bean that is a list of&lt;code&gt;PositiveStreak&lt;/code&gt;s using the &lt;code&gt;util&lt;/code&gt; namespace. We then inject this listinto a &lt;code&gt;Habit&lt;/code&gt; class called &lt;code&gt;Running&lt;/code&gt; using setter injection.&lt;/p&gt;&lt;p&gt;For setter injection to work from XML, the setter method needs to follow&lt;a href=&#34;https://en.wikipedia.org/wiki/JavaBeans#JavaBean_conventions&#34;&gt;Java beanconventions&lt;/a&gt;.This means that there should be setter method called&lt;code&gt;set&amp;lt;PropertyName&amp;gt;&lt;/code&gt;, where the important part is that the method startwith &lt;code&gt;set&lt;/code&gt;. The name of the property itself can be something completelydifferent from the field it is setting, Spring does not care. Forexample, the following would work without a hitch:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Running&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Habit&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Streak&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;myStreakList&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setStreaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Streak&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;aListOfStreaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;myStreakList&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;aListOfStreaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.Running&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;streaks&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;myRunningStreaks&amp;#34;&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The only important thing is that the &lt;code&gt;name&lt;/code&gt; attribute of the &lt;code&gt;property&lt;/code&gt;element corresponds to the &lt;code&gt;Property&lt;/code&gt; part of &lt;code&gt;setProperty&lt;/code&gt; settermethod in Java. To reiterate: &lt;em&gt;The name of the field that is being setby the mutator method can be something completely different from the&lt;code&gt;name&lt;/code&gt; attribute of the &lt;code&gt;property&lt;/code&gt; element&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;But sometimes we want to be creative with our method names if that makesthe code more readable, or maybe we are dealing with code that we cannotchange. If we end up with a mutator method that does not start with&lt;code&gt;set&lt;/code&gt;, you are now in a position to appreciate that Spring will not findthis method and throws a &lt;code&gt;BeanCreationException&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;handling-mutators-with-a-non-standard-names&#34;&gt;Handling Mutators With a Non-Standard Names&lt;/h2&gt;&lt;p&gt;On StackOverflow &lt;a href=&#34;https://stackoverflow.com/questions/6718985/specify-setter-method-name-during-dependency-injection-in-spring-3-0&#34;&gt;a solution wasproposed&lt;/a&gt;that uses a&lt;a href=&#34;https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/MethodInvokingFactoryBean.html&#34;&gt;&lt;code&gt;MethodInvokingFactoryBean&lt;/code&gt;&lt;/a&gt;.But since we are invoking a method that does not return a result, thedocumentation recommends we use&lt;a href=&#34;https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/MethodInvokingBean.html&#34;&gt;&lt;code&gt;MethodInvokingBean&lt;/code&gt;&lt;/a&gt;instead.&lt;/p&gt;&lt;p&gt;Suppose we have the follow setter in place:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Running&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Habit&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Streak&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;myStreakList&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;replaceStreaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Streak&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;aListOfStreaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;myStreakList&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;aListOfStreaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We could leverage a &lt;code&gt;MethodInvokingBean&lt;/code&gt; like so:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;running&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.Running&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;caller&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;org.springframework.beans.factory.config.MethodInvokingBean&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;targetObject&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;running&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;targetMethod&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;replaceStreaks&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;arguments&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;myRunningStreaks&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(Notice that we actually use setter injection on the&lt;code&gt;MethodInvokingBean&lt;/code&gt; by specifying properties on it.) The &lt;code&gt;targetObject&lt;/code&gt;attribute specifies on which instance a &lt;code&gt;targetMethod&lt;/code&gt; should beinvoked. We can specify our custom mutator name, and pass a reference toour arguments.&lt;/p&gt;&lt;p&gt;This is verbose, so think carefully before you violate the Java beannaming convention. Consider using a&lt;a href=&#34;https://en.wikipedia.org/wiki/Facade_pattern&#34;&gt;facade&lt;/a&gt; if the code isnot under your control. Alternatively, Java-based Spring configurationwith the &lt;code&gt;@Autowired&lt;/code&gt; annotation will work on a method with any name.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Spring Basics Wiring Beans With Xml Configuration</title>
       <link>https://relentlesscoding.com/posts/spring-basics-wiring-beans-with-xml-configuration/</link>
       <pubDate>Wed, 05 Sep 2018 19:52:43 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/spring-basics-wiring-beans-with-xml-configuration/</guid>
       <description>&lt;p&gt;If you have to work with legacy Spring applications, chances are youwill have to know how XML-based configuration works. Although Javaconfiguration is preferred for new applications, sometimes you justdon’t have a choice, so you&amp;rsquo;d better be comfortable with it.&lt;/p&gt;&lt;h2 id=&#34;the-code&#34;&gt;The Code&lt;/h2&gt;&lt;p&gt;You can find the code from this blog post on&lt;a href=&#34;https://gitlab.com/neftas/wire-spring-beans-xml&#34;&gt;GitLab&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&#34;dependencies&#34;&gt;Dependencies&lt;/h2&gt;&lt;p&gt;The only dependency you need to get a Spring container running is&lt;code&gt;spring-context&lt;/code&gt;. Add in Maven:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-context&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.0.5.RELEASE&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;declaring-beans-in-xml&#34;&gt;Declaring Beans in XML&lt;/h2&gt;&lt;p&gt;The following class will become a bean:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;com.relentlesscoding.wirebeans&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;java.util.List&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Running&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Habit&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Running&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Run 10 km every day&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Streak&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getDescription&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Streak&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getStreaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setStreaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Streak&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;addStreak&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Streak&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streak&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streak&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create an XML file, give it any name (I would call it&lt;code&gt;applicationContext.xml&lt;/code&gt;) and place it in your &lt;code&gt;src/main/resources&lt;/code&gt;.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34; ?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;beans&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;xmlns=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/beans&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/beans&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;            http://www.springframework.org/schema/beans/spring-beans.xsd &amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;running&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.Running&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/beans&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This would wire a bean with the id &lt;code&gt;running&lt;/code&gt; that is of type &lt;code&gt;Running&lt;/code&gt;(which is a &lt;code&gt;Habit&lt;/code&gt;). You can see why this is much more of a hassle toset up than using a simple &lt;code&gt;@Component&lt;/code&gt; annotation on a class or a&lt;code&gt;@Bean&lt;/code&gt; annotation in a &lt;code&gt;@Configuration&lt;/code&gt; class. Those XML namespaces arenasty, but luckily most IDEs will help you with them.&lt;/p&gt;&lt;h2 id=&#34;injecting-beans&#34;&gt;Injecting Beans&lt;/h2&gt;&lt;p&gt;To inject dependencies, we have two choices:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Constructor injection&lt;/li&gt;&lt;li&gt;Setter injection&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Unlike with Java configuration, we cannot insert into fields when usingXML-based configuration.&lt;/p&gt;&lt;h3 id=&#34;constructor-injection&#34;&gt;Constructor Injection&lt;/h3&gt;&lt;p&gt;Suppose we want to insert one bean into another bean, for instance a&lt;code&gt;HabitRepository&lt;/code&gt; that persists habits to the database into a&lt;code&gt;HabitService&lt;/code&gt;. By using the &lt;code&gt;&amp;lt;constructor-arg /&amp;gt;&lt;/code&gt; element and the &lt;code&gt;ref&lt;/code&gt;property, we can accomplish this:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;habitRepository&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.HabitRepository&amp;#34;&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;habitService&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.HabitService&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;constructor-arg&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;habitRepository&amp;#34;&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we wanted to pass more arguments to the &lt;code&gt;HabitService&lt;/code&gt; constructor,we must keep on eye on the order: it must be the same as the order inwhich they are declared in the class.&lt;/p&gt;&lt;p&gt;To ease working with constructor arguments and as a way to curtail theverbosity of the XML configuration, Spring offers the &lt;code&gt;c&lt;/code&gt; XML namespaceto help wire beans without the need to create a sub-element&lt;code&gt;&amp;lt;constructor-arg /&amp;gt;&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34; ?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;beans&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;xmlns=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/beans&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xmlns:c=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/c&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/beans&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;       http://www.springframework.org/schema/beans/spring-beans.xsd&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;currentTime&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;java.time.LocalDate&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;factory-method=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;now&amp;#34;&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;streak&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.PositiveStreak&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;na&#34;&gt;c:startTime-ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;currentTime&amp;#34;&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We define a bean of type &lt;code&gt;java.time.LocalDate&lt;/code&gt; and we use the staticfactory method &lt;code&gt;now()&lt;/code&gt; to get an instance of it. We then use the &lt;code&gt;c&lt;/code&gt;namespace to pass it to the constructor of our &lt;code&gt;PositiveStreak&lt;/code&gt; bean.&lt;code&gt;c:startTime-ref=&amp;quot;currentTime&amp;quot;&lt;/code&gt; should be read as: pass the reference tothe bean with id &lt;code&gt;currentTime&lt;/code&gt; to the constructor argument that has thename &lt;code&gt;startTime&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;So we can reference constructor arguments by name. We can also referencethem by position. &lt;code&gt;c:_0-ref=&amp;quot;currentTime&amp;quot;&lt;/code&gt; would do the exact samething. XML does not allow a digit as the first character of anattribute, so we have to use an underscore. If there is only a singleargument to the constructor, we can even use the shorthand&lt;code&gt;c:_-ref=&amp;quot;currentTime&amp;quot;&lt;/code&gt;. I would not want to promote this as readable,but it’s good to know it exists and might be used in the wild.&lt;/p&gt;&lt;p&gt;Read more about the &lt;code&gt;c&lt;/code&gt; namespace&lt;a href=&#34;https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-c-namespace&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&#34;setter-injection&#34;&gt;Setter Injection&lt;/h3&gt;&lt;p&gt;To use setter injection with XML-based configuration, you use the&lt;code&gt;&amp;lt;property&amp;gt;&lt;/code&gt; element. If you look back at the &lt;code&gt;Running&lt;/code&gt; class above, yousee it has a method &lt;code&gt;setStreak&lt;/code&gt; that takes a &lt;code&gt;List&amp;lt;Streak&amp;gt;&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;startTime&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;java.time.LocalDate&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;factory-method=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;now&amp;#34;&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;streak&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.PositiveStreak&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;c:_0-ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;startTime&amp;#34;&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;running&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.Running&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;streaks&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;list&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;lt;ref&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;bean=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;streak&amp;#34;&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/list&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;&amp;lt;property&amp;gt;&lt;/code&gt; element has an attribute &lt;code&gt;name&lt;/code&gt; that refers to thefield name of the bean being set. In the current case, class &lt;code&gt;Running&lt;/code&gt;has a field named &lt;code&gt;streaks&lt;/code&gt;. As a child element of &lt;code&gt;&amp;lt;property&amp;gt;&lt;/code&gt; wedefine a list of &lt;code&gt;Streak&lt;/code&gt; references.&lt;/p&gt;&lt;p&gt;For a list of literal String values, this would have looked like:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;listOfStrings&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;list&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;value&amp;gt;&lt;/span&gt;string value 1&lt;span class=&#34;nt&#34;&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;value&amp;gt;&lt;/span&gt;string value 1&lt;span class=&#34;nt&#34;&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;value&amp;gt;&amp;lt;null/&amp;gt;&amp;lt;/value&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/list&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The list is wired with literal &lt;code&gt;String&lt;/code&gt;s and even a literal &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Spring also provides the &lt;code&gt;p&lt;/code&gt; namespace to make this more convenient(less verbose):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;beans&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;xmlns=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/beans&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xmlns:c=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/c&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xmlns:p=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/p&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/beans&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;       http://www.springframework.org/schema/beans/spring-beans.xsd&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;startTime&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;java.time.LocalDate&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;factory-method=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;now&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;streak&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.PositiveStreak&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;na&#34;&gt;c:_0-ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;startTime&amp;#34;&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;running&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.Running&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;na&#34;&gt;p:streaks-ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;streak&amp;#34;&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/beans&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The usage of the &lt;code&gt;p&lt;/code&gt; namespace is a lot like that of the &lt;code&gt;c&lt;/code&gt; namespace.In this case, &lt;code&gt;p:streaks-ref=&amp;quot;streak&amp;quot;&lt;/code&gt; tells spring to wire a propertynamed &lt;code&gt;streaks&lt;/code&gt; with the bean that is referenced by the id &lt;code&gt;streak&lt;/code&gt;.Now, the property &lt;code&gt;streaks&lt;/code&gt; takes a &lt;code&gt;List&lt;/code&gt;. If we pass only a singleelement to that list, the current syntax works and Spring will happilyinsert the single reference to &lt;code&gt;streak&lt;/code&gt; into a &lt;code&gt;List&lt;/code&gt; for us and passthat to the setter method. If we want to pass more than one element in alist, however, we have to create the list separately first, and thenpass the id of that reference to the &lt;code&gt;p&lt;/code&gt; property:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;beans&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;xmlns=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/beans&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xmlns:c=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/c&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xmlns:p=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/p&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xmlns:util=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/util&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;na&#34;&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.springframework.org/schema/beans&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;       http://www.springframework.org/schema/beans/spring-beans.xsd&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;       http://www.springframework.org/schema/util&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;       http://www.springframework.org/schema/util/spring-util.xsd&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;startTime&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;java.time.LocalDate&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;factory-method=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;now&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;firstStreak&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.PositiveStreak&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;c:_0-ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;startTime&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;secondStreak&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.PositiveStreak&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;c:_0-ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;startTime&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;util:list&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;myRunningStreak&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;ref&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;bean=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;firstStreak&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;ref&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;bean=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;secondStreak&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/util:list&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;running&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding.wirebeans.Running&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;p:streaks-ref=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;myRunningStreak&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/beans&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We need to add the &lt;code&gt;util&lt;/code&gt; namespace and the location of the &lt;code&gt;util&lt;/code&gt;schema definition to get this to work. You see that the XML becomesquite verbose the more you try to do with it. The &lt;code&gt;util&lt;/code&gt; namespaceallows us to create collections of literal values or beans. Thesecollections can then be referenced by their &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;More information:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-p-namespace&#34;&gt;Read more about the &lt;code&gt;p&lt;/code&gt;namespace&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#xsd-schemas-util&#34;&gt;Read more about the &lt;code&gt;util&lt;/code&gt;namespace&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;taking-the-app-for-a-test-ride&#34;&gt;Taking the App for a Test Ride&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;com.relentlesscoding.wirebeans&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.junit.Assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.junit.Test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.junit.runner.RunWith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.beans.factory.annotation.Autowired&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.test.context.ContextConfiguration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.test.context.junit4.SpringRunner&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@RunWith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SpringRunner&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@ContextConfiguration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;locations&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;classpath:applicationContext.xml&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;HabitTest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Autowired&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Habit&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;runningHabit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;runningHabitIsNotNull&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;assertNotNull&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;runningHabit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;runningHabitHasSingleStreak&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;assertEquals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;runningHabit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getStreaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To run integration tests where the Spring context is available, you needthe following dependency:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-test&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.0.5.RELEASE&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class=&#34;nt&#34;&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;spring-test&lt;/code&gt; contains the &lt;code&gt;SpringRunner&lt;/code&gt; JUnit runner, and the&lt;code&gt;@ContextConfiguration&lt;/code&gt; that will tell Spring where to look for theapplication context that contains the beans that need to be wired. Inthis case, we tell it to look at the &lt;code&gt;applicationContext.xml&lt;/code&gt; that weput in &lt;code&gt;src/main/resources&lt;/code&gt;, so we can reference it by looking at theroot of our classpath with the attribute &lt;code&gt;locations = &amp;quot;classpath:applicationContext.xml&amp;quot;&lt;/code&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Quick Vim Tip: Change Text Between White Space</title>
       <link>https://relentlesscoding.com/posts/quick-tip-change-text-between-white-space/</link>
       <pubDate>Sun, 02 Sep 2018 17:00:22 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/quick-tip-change-text-between-white-space/</guid>
       <description>&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When the cursor is on the &lt;code&gt;S&lt;/code&gt; of &lt;code&gt;String&lt;/code&gt;, use &lt;code&gt;ciW&lt;/code&gt; to delete&lt;code&gt;List&amp;lt;String&amp;gt;&lt;/code&gt; and start insert. Obviously, you can use any othercommand with that movement, for example yank the text (&lt;code&gt;yiW&lt;/code&gt;) or deleteit (&lt;code&gt;diW&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;From &lt;code&gt;:help WORD&lt;/code&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;A WORD consists of a sequence of non-blank characters, separated withwhite space. An empty line is also considered to be a WORD.&lt;/p&gt;&lt;/blockquote&gt;</description>
     </item>
   
     <item>
       <title>Spring Basics Wiring and Injecting Beans With Java Configuration</title>
       <link>https://relentlesscoding.com/posts/spring-basics-wiring-and-injecting-beans-with-java-configuration/</link>
       <pubDate>Sun, 02 Sep 2018 16:48:04 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/spring-basics-wiring-and-injecting-beans-with-java-configuration/</guid>
       <description>&lt;p&gt;For new projects, Java configuration is preferred over XML-basedconfiguration. In this post, we&amp;rsquo;re going to look at how to configure Spring withconfiguration in Java, instead of the traditional XML.&lt;/p&gt;&lt;p&gt;For XML-based configuration, see a future blog post.&lt;/p&gt;&lt;h2 id=&#34;the-code&#34;&gt;The code&lt;/h2&gt;&lt;p&gt;You can find the code from this blog post on&lt;a href=&#34;https://gitlab.com/neftas/wire-spring-beans&#34;&gt;GitLab&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&#34;dependencies&#34;&gt;Dependencies&lt;/h2&gt;&lt;p&gt;The only dependency you need to get a Spring container running is&lt;code&gt;spring-context&lt;/code&gt;. Add in Maven:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-context&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.0.5.RELEASE&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;detecting-beans-by-scanning-for-components&#34;&gt;Detecting beans by scanning for components&lt;/h2&gt;&lt;p&gt;Create a class (which can have any name, here I chose &lt;code&gt;AppConfig&lt;/code&gt;) andannotate it with &lt;code&gt;@Configuration&lt;/code&gt; and &lt;code&gt;@ComponentScan&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;com.relentlesscoding.wirebeans&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.context.annotation.ComponentScan&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.context.annotation.Configuration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Configuration&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@ComponentScan&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;AppConfig&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default, the &lt;code&gt;@ComponentScan&lt;/code&gt; will recursively scan the package inwhich the &lt;code&gt;AppConfig&lt;/code&gt; class is declared. To change this, you can add apackage to the &lt;code&gt;value&lt;/code&gt; element(&lt;code&gt;@ComponentScan(&amp;quot;com.relentlesscoding.wirebeans.beans&amp;quot;)&lt;/code&gt;) to scan onlythat package. If it troubles you that this is not type safe and hindersrefactoring (it is a simple string after all), you can also pass &lt;code&gt;Class&lt;/code&gt;objects to the &lt;code&gt;basePackageClasses&lt;/code&gt; element. It will then scan thepackage that class is part of. Some people even recommend to createmarker interfaces in each package for this purpose, but I think thisclutters up the source code too much.&lt;/p&gt;&lt;h2 id=&#34;three-ways-to-declare-beans&#34;&gt;Three ways to declare beans&lt;/h2&gt;&lt;p&gt;Now Spring is able to detect our beans. We can declare beans in threeways:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;By annotating a class with &lt;code&gt;@Component&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;By annotating a method with a non-&lt;code&gt;void&lt;/code&gt; return type with &lt;code&gt;@Bean&lt;/code&gt; ina class annotated with &lt;code&gt;@Configuration&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;By annotating any method with a non-&lt;code&gt;void&lt;/code&gt; return type with &lt;code&gt;@Bean&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&#34;automatic-configuration-with-component&#34;&gt;Automatic configuration with &lt;code&gt;@Component&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;The simplest way to declare a bean is by annotating a class with&lt;code&gt;@Component&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Running&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Habit&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Streak&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// accessors omitted for brevity&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we do not specify the &lt;code&gt;value&lt;/code&gt; element of &lt;code&gt;@Component&lt;/code&gt;, the id of thebean will be the lowercase name of the class, in this case &lt;code&gt;running&lt;/code&gt;. Wecan use this id elsewhere to specify this particular bean, shouldambiguities arise.&lt;/p&gt;&lt;h3 id=&#34;explicit-configuration-in-class-annotated-with-configuration&#34;&gt;Explicit configuration in class annotated with &lt;span class=&#34;citation&#34; data-cites=&#34;Configuration&#34;&gt;@Configuration&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;If you have control over the beans you are creating, i.e. you arewriting the source code, you would always go with automaticconfiguration by annotating your bean classes with &lt;code&gt;@Component&lt;/code&gt;. If youare creating a bean for a class from a library, you can define yourbeans in your &lt;code&gt;AppConfig&lt;/code&gt; class:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Configuration&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@ComponentScan&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;AppConfig&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Bean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Streak&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Streak&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ArrayList&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PositiveStreak&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalDate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here, we defined a bean of type &lt;code&gt;List&amp;lt;Streak&amp;gt;&lt;/code&gt; that we can now injectinto any other bean by using the &lt;code&gt;@Autowired&lt;/code&gt; annotation (see below).&lt;/p&gt;&lt;h3 id=&#34;lite-beans&#34;&gt;Lite Beans&lt;/h3&gt;&lt;p&gt;Actually, we can declare any method with a non-&lt;code&gt;void&lt;/code&gt; return type to bea &lt;code&gt;@Bean&lt;/code&gt;. If we declare a bean outside of a configuration class, itwill become a “lite bean”. Spring will still manage its life cycle andscope and we can still autowire the bean into other beans, but wheninvoking the method directly, it will just be a plain-old Java methodinvocation without Spring magic. (Normally, Spring would create a proxyaround the bean and all invocations would go through the Springcontainer. This would mean that by default only a single instance of thebean would exist, for example. In “lite” mode, however, the annotatedmethod is just a factory method, and will happily instantiate a newobject every time it is called.)&lt;/p&gt;&lt;p&gt;Read more about lite beans&lt;a href=&#34;https://docs.spring.io/spring/docs/5.0.4.RELEASE/spring-framework-reference/core.html#beans-java-basic-concepts&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&#34;using-the-declared-beans&#34;&gt;Using the declared beans&lt;/h2&gt;&lt;p&gt;To use Spring’s dependency injection, you have a couple of options, allof which involve annotating a method or field with &lt;code&gt;@Autowired&lt;/code&gt;. Bydefault, a matching bean of the specified type needs to exist in theSpring context or else Spring will throw an exception. To make theinjection optional, set the &lt;code&gt;required&lt;/code&gt; element of &lt;code&gt;@Autowired&lt;/code&gt; to&lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&#34;constructor-injection&#34;&gt;Constructor injection&lt;/h3&gt;&lt;p&gt;For mandatory dependencies, you should use constructor injection.“Mandatory” means the bean would not make sense without the bean onwhich it depends. For example, a &lt;code&gt;HabitService&lt;/code&gt; persists &lt;code&gt;Habit&lt;/code&gt;s to thedatabase. So a DAO or repository would be a mandatory dependency.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;HabitService&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HabitRepository&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;habitRepository&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Autowired&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Running&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HabitRepository&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;habitRepository&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;habitRepository&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;habitRepository&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;field-injection&#34;&gt;Field injection&lt;/h3&gt;&lt;p&gt;Field injection should not be preferred, because it makes testing(e.g. with mocks) harder to pull off. In the following example weinject a dependency into a private field. When we would try to mock thedependency, we would have to deal with the access restriction.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;HabitService&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Autowired&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HabitRepository&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;habitRepository&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;setter-injection&#34;&gt;Setter injection&lt;/h3&gt;&lt;p&gt;A third way to inject dependencies is through setter injection. Putting&lt;code&gt;@Autowired&lt;/code&gt; on any method with one or more parameters will make Springlook for appropriate bean candidates in the Spring context.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Running&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Habit&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Running&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Run 10 km every day&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Streak&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Autowired&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setStreaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Streak&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;streaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;taking-the-application-for-a-test-run&#34;&gt;Taking the application for a test run&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;com.relentlesscoding.wirebeans&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.junit.Assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.junit.Test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.junit.runner.RunWith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.beans.factory.annotation.Autowired&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.test.context.ContextConfiguration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.test.context.junit4.SpringRunner&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@RunWith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SpringRunner&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@ContextConfiguration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;classes&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AppConfig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;HabitTest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Autowired&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Habit&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;runningHabit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;runningHabitIsNotNull&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;assertNotNull&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;runningHabit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;runningHabitHasSingleStreak&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;assertEquals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;runningHabit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getStreaks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can specify the application context by using the&lt;code&gt;@ContextConfiguration&lt;/code&gt; annotation and filling in the &lt;code&gt;classes&lt;/code&gt; element.The JUnit 4 annotation &lt;code&gt;@RunWith&lt;/code&gt; specifies the &lt;code&gt;SpringRunner.class&lt;/code&gt;(which is a convenience extension of the longer&lt;code&gt;SpringJUnit4ClassRunner&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;&lt;code&gt;@ContextConfiguration&lt;/code&gt; is part of the &lt;code&gt;spring-test&lt;/code&gt; library:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-test&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.0.5.RELEASE&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class=&#34;nt&#34;&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Use Vidir to Quickly Edit Filenames in Your Editor</title>
       <link>https://relentlesscoding.com/posts/use-vidir-to-quickly-edit-filenames-in-your-editor/</link>
       <pubDate>Mon, 20 Aug 2018 20:10:00 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/use-vidir-to-quickly-edit-filenames-in-your-editor/</guid>
       <description>&lt;p&gt;If you have installed &lt;code&gt;moreutils&lt;/code&gt; (see below), you can type &lt;code&gt;vidir&lt;/code&gt; toopen up the current working directory in your &lt;code&gt;$EDITOR&lt;/code&gt;. You can use allthe power of your editor to edit and/or delete filenames anddirectories. Editing a line will rename the file or directory, deletinga line will remove the file or directory.&lt;/p&gt;&lt;p&gt;The following will list all your JPEG pictures in the current directoryin your editor:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ vidir *.jpeg&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;vidir&lt;/code&gt; is not recursive by default: if you want to recursively editfilenames, you can do:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ find -type f -name &amp;#39;*.jpeg&amp;#39; | vidir -  # take note of the trailing dash -&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;deleting-non-empty-directories&#34;&gt;Deleting non-empty directories&lt;/h2&gt;&lt;p&gt;When trying to delete a non-empty directory, &lt;code&gt;vidir&lt;/code&gt; will complain:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/usr/bin/vidir: failed to remove ./non-empty-directory: Directory not empty&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can use &lt;code&gt;find&lt;/code&gt; again:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ls -1 non-empty-dirfile1.txtfile2.txt$ find | vidir -1   ./non-empty-dir2   ./non-empty-dir/file1.txt3   ./non-empty-dir/file2.txt&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When we delete all the files from the directory and the directoryitself, the directory &lt;em&gt;will&lt;/em&gt; be deleted.&lt;/p&gt;&lt;p&gt;To see what &lt;code&gt;vidir&lt;/code&gt; is actually doing, you can pass it the &lt;code&gt;-v&lt;/code&gt; or&lt;code&gt;--verbose&lt;/code&gt; flag:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ find | vidir -v -removed &amp;#39;./non-empty-dir/file2.txt&amp;#39;removed &amp;#39;./non-empty-dir/file1.txt&amp;#39;removed &amp;#39;./non-empty-dir&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;how-to-install&#34;&gt;How to install&lt;/h2&gt;&lt;p&gt;In Arch Linux, you can install the &lt;code&gt;moreutils&lt;/code&gt; package with &lt;code&gt;sudo pacman -S moreutils&lt;/code&gt;. On Debian distros, you can run &lt;code&gt;sudo apt install moreutils&lt;/code&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Use Pandoc With Pygments to Highlight Source Code</title>
       <link>https://relentlesscoding.com/posts/use-pandoc-with-pygments-to-highlight-source-code/</link>
       <pubDate>Sun, 12 Aug 2018 19:32:38 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/use-pandoc-with-pygments-to-highlight-source-code/</guid>
       <description>&lt;p&gt;I am someone who has JavaScript disabled by default in his browser (Iuse &lt;a href=&#34;https://github.com/gorhill/uMatrix&#34;&gt;uMatrix&lt;/a&gt; in Firefox for that).Only when I trust a site and I need to use functionality that trulydepends on JavaScript will I turn it on. This hopefully protects me frommost of the known and unknown bad stuff out there on the internet. Italso makes me appreciate people who go through the trouble of makingtheir webpages work without JavaScript.&lt;/p&gt;&lt;p&gt;Until recently, I used a JavaScript plugin on this blog to format sourcecode. This bothered me, since using JavaScript just to display somesource code seems like overkill and makes people have to turn onJavaScript in their browsers just to see the source code formattednicely. I wanted to do better than that.&lt;/p&gt;&lt;p&gt;The way I normally write my blog posts is, I start with a Markdownarticle and then use &lt;a href=&#34;https://pandoc.org/&#34;&gt;&lt;code&gt;pandoc&lt;/code&gt;&lt;/a&gt; to convert it toHTML which I then copy and paste into Wordpress (if there is a betterway to do this, please contact me). I noticed &lt;code&gt;pandoc&lt;/code&gt; provides a switch&lt;code&gt;--filter&lt;/code&gt; where you can specify a executable that can transform the&lt;code&gt;pandoc&lt;/code&gt; output. The only problem is, you have to write a filter.Luckily, &lt;a href=&#34;https://gist.github.com/fizruk/6620756&#34;&gt;I found a GitHub gist&lt;/a&gt;that has already figured out how to write one. Here is some Haskell foryou:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-haskell&#34; data-lang=&#34;haskell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Text.Pandoc.Definition&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Text.Pandoc.JSON&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;toJSONFilter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Text.Pandoc.Shared&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Data.Char&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;toLower&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;System.Process&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;readProcess&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;System.IO.Unsafe&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;toJSONFilter&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;highlight&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;highlight&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Block&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Block&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;highlight&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;CodeBlock&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;options&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;RawBlock&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Format&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;html&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pygments&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;highlight&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;pygments&lt;/span&gt;&lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;pygments&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;length&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;unsafePerformIO&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;readProcess&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;pygmentize&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;-l&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;toLower&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;head&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)),&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;&amp;#34;-f&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;html&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;length&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;unsafePerformIO&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;readProcess&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;pygmentize&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;-l&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;toLower&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;head&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)),&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-O linenos=inline&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;&amp;#34;-f&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;html&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;otherwise&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;lt;div class =&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&amp;#34;&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;highlight&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&amp;#34;&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;gt;&amp;lt;pre&amp;gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that this program invokes another program, &lt;code&gt;pygmentize&lt;/code&gt; to actuallyhighlight the source code (&lt;code&gt;pygmentize&lt;/code&gt; is part of the&lt;a href=&#34;http://pygments.org/&#34;&gt;Pygments&lt;/a&gt; project). So, install &lt;code&gt;pygmentize&lt;/code&gt; withyour favorite package manager, install Haskell if you have not done soalready, and then compile &lt;code&gt;pygments.hs&lt;/code&gt; with:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ghc -dynamic pygments.hs&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That’s it! Putting it all together, to create a blog post, I can nowdo:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ pandoc -F pygments -f markdown -t html5 -o blogpost.html blogpost.md&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I added some CSS that makes use of the Pygments classes and voilà: youcan now view this blog without having to worry about a JavaScriptcryptocurrency miner hijacking your CPU. You’re welcome.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Remove All Files Except a Few in Bash</title>
       <link>https://relentlesscoding.com/posts/remove-all-files-except-a-few-in-bash/</link>
       <pubDate>Sun, 12 Aug 2018 18:50:05 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/remove-all-files-except-a-few-in-bash/</guid>
       <description>&lt;p&gt;How can remove &lt;em&gt;most&lt;/em&gt; of the files in a directory in Bash?&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; ls -1&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;153390909910_first&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;15339090991_second&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;15339090992_third&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;15339090993_fourth&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;15339090994_fifth&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;15339090995_sixth&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;15339090996_seventh&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;15339090997_eighth&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;15339090998_nineth&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;15339090999_tenth&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;15339091628_do_not_delete&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;root&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;root.sql&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We want to delete all files that start with a timestamp (seconds sincethe epoch), except the newest file (&lt;code&gt;15339091628_do_not_delete&lt;/code&gt;) and thefiles &lt;code&gt;root&lt;/code&gt; and &lt;code&gt;root.sql&lt;/code&gt;. The easiest way to do this, is enabling theshell option &lt;code&gt;extglob&lt;/code&gt; (“extended globbing”), which allows us to usepatterns to include or exclude files of operations:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;shopt&lt;/span&gt; -s extglob&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; rm !&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;*do_not_delete&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;root*&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The last command will tell Bash to remove all files, except the onesthat match either one of the patterns (everything ending with&lt;code&gt;do_not_delete&lt;/code&gt; and everything starting with &lt;code&gt;root&lt;/code&gt;). We delimit thepatterns by using a pipe character &lt;code&gt;|&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Other patterns that are supported by &lt;code&gt;extglob&lt;/code&gt; include:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;?(pattern-list)      Matches zero or one occurrence of the given patterns\*(pattern-list)      Matches zero or more occurrences of the given patterns+(pattern-list)      Matches one or more occurrences of the given patterns@(pattern-list)      Matches one of the given patterns!(pattern-list)      Matches anything except one of the given patterns&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To disable the extended globbing again:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;shopt&lt;/span&gt; -u extglob&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;&lt;p&gt;To read about all the options that &lt;code&gt;extglob&lt;/code&gt; gives you, refer to &lt;code&gt;man bash&lt;/code&gt; (search for &lt;em&gt;Pathname Expansion&lt;/em&gt;). Searching for &lt;code&gt;shopt&lt;/code&gt; in thesame manual page will turn up all shell options. To see which shelloptions are currently enables for your shell, type &lt;code&gt;shopt -p&lt;/code&gt; at theprompt.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Tomcat7 Maven Plugin Invalid Byte Tag in Constant Pool 19</title>
       <link>https://relentlesscoding.com/posts/tomcat7-maven-plugin-invalid-byte-tag-in-constant-pool-19/</link>
       <pubDate>Tue, 24 Apr 2018 07:11:48 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/tomcat7-maven-plugin-invalid-byte-tag-in-constant-pool-19/</guid>
       <description>&lt;p&gt;I use &lt;code&gt;tomcat7-maven-plugin&lt;/code&gt; to spin up a Tomcat 7 container where I canrun my web application. When I added dependencies for log4j2 (version2.11.0) to my project, I got the error:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;org.apache.tomcat.util.bcel.classfile.ClassFormatException:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Invalid byte tag in constant pool: 19&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Apparently, &lt;code&gt;log4j2&lt;/code&gt; is a multi-release jar and older versions of Tomcatcan’t handle that. So I needed to upgrade my Tomcat maven plugin.&lt;/p&gt;&lt;h2 id=&#34;solution-update-your-tomcat&#34;&gt;Solution: Update your Tomcat&lt;/h2&gt;&lt;p&gt;But how do you update your Tomcat? Its &lt;a href=&#34;https://tomcat.apache.org/maven-plugin-2.0/index.html&#34;&gt;informationpage&lt;/a&gt; shows thatit hasn’t been updated for a while, the latest version being 2.2 whichruns 7.0.47 by default. &lt;a href=&#34;https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.apache.tomcat%22%20AND%20a%3A%22tomcat%22&#34;&gt;MavenCentral&lt;/a&gt;,on the other hand, shows that the latest version at the moment of thiswriting is 7.0.86. That’s the version we want.&lt;/p&gt;&lt;p&gt;Change your &lt;code&gt;pom.xml&lt;/code&gt; in the following way:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;project&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;tomcat.version&amp;gt;&lt;/span&gt;7.0.86&lt;span class=&#34;nt&#34;&gt;&amp;lt;/tomcat.version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat.maven&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat7-maven-plugin&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.2&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;path&amp;gt;&lt;/span&gt;/&lt;span class=&#34;nt&#34;&gt;&amp;lt;/path&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;port&amp;gt;&lt;/span&gt;7777&lt;span class=&#34;nt&#34;&gt;&amp;lt;/port&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat.embed&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-embed-core&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-util&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-coyote&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-api&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-jdbc&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-dbcp&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-servlet-api&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-jsp-api&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-jasper&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-jasper-el&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-el-api&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-catalina&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-tribes&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-catalina-ha&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-annotations-api&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-juli&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat.embed&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-embed-logging-juli&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.tomcat.embed&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tomcat-embed-logging-log4j&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${tomcat.version}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>How to Write a Custom Appender in Log4j2</title>
       <link>https://relentlesscoding.com/posts/how-to-write-a-custom-appender-in-log4j2/</link>
       <pubDate>Sat, 21 Apr 2018 13:40:59 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/how-to-write-a-custom-appender-in-log4j2/</guid>
       <description>&lt;p&gt;Ever wanted to test whether a log statement is triggered? Or whether the formatis the way you want? In this post, we&amp;rsquo;re going to create a custom log appenderso we can be sure logging is behaving the way we expect.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* package declaration, imports... */&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Plugin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;CustomListAppender&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;category&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Core&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;CATEGORY_NAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;elementType&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Appender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ELEMENT_TYPE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;printObject&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;CustomListAppender&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AbstractAppender&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// for storing the log events&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LogEvent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;events&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ArrayList&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;protected&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;CustomListAppender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Filter&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Layout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Serializable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;layout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ignoreExceptions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;layout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ignoreExceptions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LogEvent&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;instanceof&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MutableLogEvent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;events&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MutableLogEvent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;createMemento&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;events&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LogEvent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getEvents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;events&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@PluginFactory&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CustomListAppender&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;createAppender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@PluginAttribute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@PluginElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Layout&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Layout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Serializable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;layout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@PluginElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Filter&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Filter&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LOGGER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;No name provided for TestLoggerAppender&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;layout&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;layout&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PatternLayout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;createDefaultLayout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CustomListAppender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;layout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our &lt;code&gt;CustomListAppender&lt;/code&gt; extends &lt;code&gt;AbstractAppender&lt;/code&gt;, because thatimplements a lot of the methods from the &lt;code&gt;Appender&lt;/code&gt; interface for usthat we would otherwise have to implement ourselves.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;@Plugin&lt;/code&gt; annotation identifies this class a plugin that should bepicked up by the &lt;code&gt;PluginManager&lt;/code&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The &lt;code&gt;name&lt;/code&gt; attribute defines the name of the appender that can beused in the configuration.&lt;/li&gt;&lt;li&gt;The &lt;code&gt;category&lt;/code&gt; attribute should be &lt;code&gt;&amp;quot;Core&amp;quot;&lt;/code&gt;, because “Core pluginsare those that are directly represented by an element in aconfiguration file, such as an Appender, Layout, Logger or Filter”(&lt;a href=&#34;https://logging.apache.org/log4j/2.x/manual/plugins.html#Core&#34;&gt;source&lt;/a&gt;).And we are creating an appender.&lt;/li&gt;&lt;li&gt;The &lt;code&gt;elementType&lt;/code&gt; attribute defines which type of element in theCore category this plugin should be. In our case, &lt;code&gt;&amp;quot;appender&amp;quot;&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;The &lt;code&gt;printObject&lt;/code&gt; attribute defines whether our custom plugin classdefines a useful &lt;code&gt;toString()&lt;/code&gt; method. We do, because the&lt;code&gt;AbstractAppender&lt;/code&gt; class we’re extending is taking care of that forus.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We implement the &lt;code&gt;Appender#append(LogEvent)&lt;/code&gt; method to add each event toour &lt;code&gt;events&lt;/code&gt; list. If the &lt;code&gt;LogEvent&lt;/code&gt; happens to be mutable, we must takecare to create an immutable copy of the event, otherwise subsequent logevents will overwrite it (we will get a list of, say, three log eventsthat are all referencing the same object). We also add a simple gettermethod to retrieve all log events.&lt;/p&gt;&lt;p&gt;For the &lt;code&gt;PluginManager&lt;/code&gt; to create our custom plugin, it needs a way toinstantiate it. log4j2 uses a factory method for that, indicated by theannotation &lt;code&gt;@PluginFactory&lt;/code&gt;. An appender contains attributes, such as aname, and other elements, such as layouts and filters. To allow forthese, we use the corresponding annotations &lt;code&gt;@PluginAttribute&lt;/code&gt; toindicate that a parameter represents an attribute, and &lt;code&gt;@PluginElement&lt;/code&gt;to indicate that a parameter represents an element.&lt;/p&gt;&lt;p&gt;To log errors that might occur during this setup, we can make use of the&lt;code&gt;StatusLogger&lt;/code&gt;. This logger is available as &lt;code&gt;LOGGER&lt;/code&gt;, and is defined inone of the parents of our custom plugin, &lt;code&gt;AbstractLifeCycle&lt;/code&gt;. (The levelof log messages that should be visible can be adjusted in the&lt;code&gt;&amp;lt;Configuration status=&amp;quot;warn&amp;quot; ...&amp;gt;&lt;/code&gt; element.)&lt;/p&gt;&lt;h2 id=&#34;configuration&#34;&gt;Configuration&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;Configuration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;packages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;com.relentlesscoding.logging.plugins&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;warn&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;appenders&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;STDOUT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;CustomListAppender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;MyVeryOwnListAppender&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Loggers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;logger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;-&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;com.relentlesscoding.logging&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;level&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;AppenderRef&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;MyVeryOwnListAppender&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;level&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;AppenderRef&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;STDOUT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;packages&lt;/code&gt; attribute on the &lt;code&gt;Configuration&lt;/code&gt; element indicates thepackage that should be scanned by the &lt;code&gt;PluginManager&lt;/code&gt; for custom pluginsduring initialization.&lt;/p&gt;&lt;h2 id=&#34;how-to-use-our-custom-list-appender&#34;&gt;How to use our custom list appender?&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CustomListAppender&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;appender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Before&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setupLogging&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LoggerContext&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LoggerContext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getContext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Configuration&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;configuration&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getConfiguration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;appender&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;configuration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getAppender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;MyVeryOwnListAppender&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;appender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getEvents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;clear&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When we run tests now, we are able to see all logged events by calling&lt;code&gt;appender.getEvents()&lt;/code&gt;. Before each test, we take care to clear the listof the previous log statements.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Unit Test Log4j2 Log Output</title>
       <link>https://relentlesscoding.com/posts/unit-test-log4j2-log-output/</link>
       <pubDate>Sat, 21 Apr 2018 11:30:19 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/unit-test-log4j2-log-output/</guid>
       <description>&lt;p&gt;Sometimes you want to test if certain log output gets generated whencertain events happen in your application. Here is how I unit test thatusing log4j2 (version 2.11.0).&lt;/p&gt;&lt;h2 id=&#34;use-loggercontextrule-to-get-to-your-listappender-quickly&#34;&gt;Use &lt;code&gt;LoggerContextRule&lt;/code&gt; to get to your &lt;code&gt;ListAppender&lt;/code&gt; quickly&lt;/h2&gt;&lt;p&gt;If you are using JUnit 4, then the quickest solution would be one thatis &lt;a href=&#34;https://logging.apache.org/log4j/2.x/manual/configuration.html#Testing_in_Maven&#34;&gt;used by log4j2itself&lt;/a&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.apache.logging.log4j.junit.LoggerContextRule&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;cm&#34;&gt;/* other imports */&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;LogEventTest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ListAppender&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;appender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@ClassRule&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LoggerContextRule&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LoggerContextRule&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;log4j2-test.yaml&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@BeforeClass&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setupLogging&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;appender&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getListAppender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;List&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Before&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;clearAppender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;appender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;clear&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;someMethodShouldLogAnError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// setup test and invoke logic&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LogEvent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;logEvents&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;appender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getEvents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;errors&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;logEvents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;stream&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getLevel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;equals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Level&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ERROR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getMessage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getFormattedMessage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;collect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Collectors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;toList&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// we logged at least one event of level error&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertThat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;errors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;greaterThanOrEqualTo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// log event message should contain &amp;#34;wrong&amp;#34; for example&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertThat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;errors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;everyItem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;containsString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;wrong&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;LoggerContextRule&lt;/code&gt; provides methods that come in handy whiletesting. Here we use the &lt;code&gt;getListAppender(...)&lt;/code&gt; method to get access toan appender that uses a list to store all log events. Before each test,we clear the list, so we have a clean slate for new log events. The testinvokes the code-under-test, requests the log events from the appenderand filters them so that we only have the error log events left. Then wethat at least one error log message was captured and that it containsthe word “wrong”.&lt;/p&gt;&lt;h2 id=&#34;use-the-loggercontext&#34;&gt;Use the &lt;code&gt;LoggerContext&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;Instead of using the class rule (which lets you conveniently pass it thefile name of the configuration), you could also use the &lt;code&gt;LoggerContext&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@BeforeClass&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setupLogging&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LoggerContext&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LoggerContext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getContext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Logger&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;logger&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getLogger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.relentlesscoding&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;appender&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ListAppender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;logger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getAppenders&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;List&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This might be your only option if you are working with JUnit 5 (andeventually you will want to migrate to that). In JUnit 5 we can’t use&lt;code&gt;LoggerContextRule&lt;/code&gt; anymore, because &lt;code&gt;@Rule&lt;/code&gt;s don’t longer exist (theywere replaced with an extension mechanism that works differently andlog4j2 doesn’t provide such an extension currently).&lt;/p&gt;&lt;h3 id=&#34;create-a-working-configuration&#34;&gt;Create a working configuration&lt;/h3&gt;&lt;p&gt;To get the examples working, we need to define an appender called “List”and a logger in our log4j2 configuration.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;log4j2-test.yaml&lt;/strong&gt;&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;Configuration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;warn&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;TestConfig&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;appenders&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;STDOUT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Loggers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;logger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;-&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;com.relentlesscoding&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;AppenderRef&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;level&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;AppenderRef&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;STDOUT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This configuration will send log events occurring in the package&lt;code&gt;com.relentlesscoding&lt;/code&gt; and sub-packages to the appender with the name“List” (which is of type &lt;code&gt;ListAppender&lt;/code&gt;). This configuration isdefined in YAML, but you can use XML, JSON or the properties format aswell.&lt;/p&gt;&lt;h2 id=&#34;maven-dependencies&#34;&gt;Maven dependencies&lt;/h2&gt;&lt;p&gt;To get the &lt;code&gt;LoggerContextRule&lt;/code&gt; in JUnit 4 working, you need thefollowing:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.logging.log4j&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;log4j-core&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.11.0&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;type&amp;gt;&lt;/span&gt;test-jar&lt;span class=&#34;nt&#34;&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To get the YAML log4j2 configuration working, you need:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.fasterxml.jackson.core&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jackson-databind&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.9.5&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.fasterxml.jackson.dataformat&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jackson-dataformat-yaml&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.9.5&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>JMockit Fakes</title>
       <link>https://relentlesscoding.com/posts/jmockit-fakes/</link>
       <pubDate>Fri, 06 Apr 2018 16:38:27 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/jmockit-fakes/</guid>
       <description>&lt;p&gt;If you are working with microservices, you may find that you are havinga lot of dependencies on other services. If you want your tests to beautonomous, that is, running in isolation of those dependencies, you caneither use a tool like &lt;a href=&#34;http://wiremock.org/&#34;&gt;WireMock&lt;/a&gt;, or you can usea mocking framework.&lt;/p&gt;&lt;p&gt;Recently, I came across &lt;a href=&#34;https://jmockit.github.io&#34;&gt;JMockit&lt;/a&gt;, a mockingframework for Java.&lt;/p&gt;&lt;p&gt;To mock external dependencies, you can use so-called “fakes”.  Say you use aservice that knows the credentials of the customers of your e-store, and youmake requests to this service by using a driver provided by this service, say&lt;code&gt;CredentialsHttpService&lt;/code&gt;. In your development environment (and Jenkins), youdon’t have access to a running service. A solution to this would be a fakeimplementation of &lt;code&gt;CredentialsHttpService&lt;/code&gt;, where we would mock the methods thatwe actually call from our code.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;CredentialsHttpService&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Optional&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CustomerAccount&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getCustomerAccount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// does HTTP request to service&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In our test code, we can now implement the fake:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ServiceTest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Tested&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Service&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;service&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MockUp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CredentialsHttpService&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AtomicInteger&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;counter&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AtomicInteger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Mock&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Optional&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CustomerAccount&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getCustomerAccount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Optional&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CustomerAccount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;builder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                                        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;accountNumber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;counter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;incrementAndGet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                                        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;service&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;doFoo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// eventually invokes our fake&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                          &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// getCustomerAccount() implementation&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The class to be faked is the type parameter of the &lt;code&gt;MockUp&lt;/code&gt; class. Thefake &lt;code&gt;CredentialsHttpService&lt;/code&gt; will only mock the methods that areannotated with &lt;code&gt;@Mock&lt;/code&gt;. All other methods of the faked class will havetheir real implementation. Mocked methods are not restricted by accessmodifiers: the method may have a private, protected, package-private orpublic access modifier, as long as the method name and number ofparameters and parameter types are the same.&lt;/p&gt;&lt;p&gt;There is a lot of other things that fakes can do in JMockit, see the&lt;a href=&#34;https://jmockit.github.io/tutorial/Faking.html&#34;&gt;documentation&lt;/a&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Bash Magic Space</title>
       <link>https://relentlesscoding.com/posts/bash-magic-space/</link>
       <pubDate>Thu, 30 Nov 2017 07:53:08 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/bash-magic-space/</guid>
       <description>&lt;p&gt;Sometimes, a little feedback is appreciated when writing some of the morecomplex Bash constructs. The &amp;ldquo;magic space&amp;rdquo; is one the things that can help usget quick feedback.&lt;/p&gt;&lt;h2 id=&#34;what-does-the-magic-space-do&#34;&gt;What does the “magic space” do?&lt;/h2&gt;&lt;p&gt;Given the following:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ find -wholename &amp;#39;*/path/to/file&amp;#39; -print -quit$ man rm$ rm -fv !-2:2&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the last line, feedback would be appreciated to see if we are indeedgoing to delete the second argument of two commands back. If you setBash’ so-called “magic space”, history expansion will take place rightaway after typing a space after &lt;code&gt;!-2:2&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ rm -fv &amp;#39;*/path/to/file&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;how-to-enable-the-magic-space&#34;&gt;How to enable the magic space?&lt;/h2&gt;&lt;p&gt;Put the following in your &lt;code&gt;~/.inputrc&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$if&lt;/span&gt; Bash&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    Space: magic-space&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$endif&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Start a new session, or use &lt;code&gt;bind -f ~/.inputrc&lt;/code&gt; to put the changes ineffect immediately.&lt;/p&gt;&lt;h2 id=&#34;other-ways-to-achieve-the-same&#34;&gt;Other ways to achieve the same&lt;/h2&gt;&lt;p&gt;You could also enable &lt;code&gt;shopt -s histverify&lt;/code&gt;, which will perform thehistory expansion and give you another opportunity to modify the commandbefore executing it. This requires you to press &lt;kbd&gt;Enter&lt;/kbd&gt;, though.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Unit Test Grails GORM Formulas</title>
       <link>https://relentlesscoding.com/posts/unit-test-grails-gorms-formulas/</link>
       <pubDate>Sun, 19 Nov 2017 11:02:49 +0100</pubDate>
       
       <guid>https://relentlesscoding.com/posts/unit-test-grails-gorms-formulas/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s take a look at how we can test GORM formulas in Grails.&lt;/p&gt;&lt;p&gt;The code for this post is &lt;a href=&#34;https://gitlab.com/neftas/pomotimer&#34;&gt;part of my &lt;code&gt;PomoTimer&lt;/code&gt; project and can befound on GitHub&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&#34;the-domain-class&#34;&gt;The domain class&lt;/h2&gt;&lt;p&gt;We have a domain class &lt;code&gt;Project&lt;/code&gt; that models a project that one isworking on when doing a particular work session. It records the name ofthe project (“Writing a blog post”), a status (“active”, “completed”), acreation time, the total time spent on this project and the user theproject belongs to.&lt;/p&gt;&lt;p&gt;The total time is a derived field: it calculates the time spent on thework sessions that belong to this project. It should only calculate thework sessions that have status “done”. Derived fields can be defined inGrails by so-called&lt;a href=&#34;http://gorm.grails.org/6.0.x/hibernate/manual/#derivedProperties&#34;&gt;formulas&lt;/a&gt;.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;com&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;relentlesscoding&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;pomotimer&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Project&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ProjectStatus&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Date&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;creationtime&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Integer&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;totaltime&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;User&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;constraints&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;blank:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;nullable:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;maxSize:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;unique:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;blank:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;nullable:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;display:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;creationtime&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;blank:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;nullable:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;totaltime&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;blank:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;nullable:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;min:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;user&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;blank:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;nullable:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;display:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mapping&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;totaltime&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;formula:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;(select ifnull(sum(select d.seconds from Duration d where ws.duration_id = d.id), 0) from Work_Session ws where ws.project_id = id and ws.status_id in (select st.id from Work_Session_Status st where st.name = \&amp;#39;done\&amp;#39;))&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;toString&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;testing-the-formula&#34;&gt;Testing the formula&lt;/h2&gt;&lt;p&gt;So how do we test a formula? Initially, I tried to write integrationtests (&lt;code&gt;grails create-integration-test &amp;lt;className&amp;gt;&lt;/code&gt; that create a classwith the &lt;code&gt;grails.testing.mixin.Integration&lt;/code&gt; and&lt;code&gt;grails.transaction.Rollback&lt;/code&gt; annotations), but these suffer from thelimitation that &lt;a href=&#34;https://docs.grails.org/latest/guide/testing.html#integrationTesting&#34;&gt;each feature method starts a newtransaction&lt;/a&gt;,and formula’s aren&amp;rsquo;t updated until after the transaction is committed(which is never because the transaction is always rolled back). Thiseffectively makes it impossible for an integration test to check whethera formula does what it is supposed to do.&lt;/p&gt;&lt;p&gt;The solution is to write a test specification that extends&lt;code&gt;HibernateSpec&lt;/code&gt;, which allows us to fully use Hibernate in our unittests.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;com&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;relentlesscoding&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;pomotimer&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;grails.test.hibernate.HibernateSpec&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ProjectTotaltimeSpec&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;HibernateSpec&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;adding a work session should increase total time&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nl&#34;&gt;given:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;a new project&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;projectStatus&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ProjectStatus&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;name:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;description:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;user&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;firstName:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            &lt;span class=&#34;nl&#34;&gt;lastName:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            &lt;span class=&#34;nl&#34;&gt;userName:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            &lt;span class=&#34;nl&#34;&gt;email:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;q@w.com&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            &lt;span class=&#34;nl&#34;&gt;password:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;b&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;project&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Project&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;name:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;a new project&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                  &lt;span class=&#34;nl&#34;&gt;status:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;projectStatus&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                  &lt;span class=&#34;nl&#34;&gt;creationtime:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Date&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(),&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                  &lt;span class=&#34;nl&#34;&gt;totaltime:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                  &lt;span class=&#34;nl&#34;&gt;user:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nl&#34;&gt;expect:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;total time of the new project is 0 seconds&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;project&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;totaltime&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nl&#34;&gt;when:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;adding a completed work session to the new project&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;duration&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Duration&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;seconds:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;987&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;done&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;WorkSessionStatus&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;name:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;done&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;description:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;done&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;WorkSession&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;starttime:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Date&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(),&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;nl&#34;&gt;duration:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;duration&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;nl&#34;&gt;project:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;project&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;nl&#34;&gt;status:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;save&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;failOnError:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;flush:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// NOTE: a refresh is required to update the domain object&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;project&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;refresh&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nl&#34;&gt;then:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;total time is equal to duration of completed work session&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;project&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;totaltime&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;987&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;interesting-links&#34;&gt;Interesting links&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://guides.grails.org/grails-mock-basics/guide/index.html#dynamicFinderServiceTests&#34;&gt;Grails documentation on&lt;code&gt;HibernateSpec&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://docs.grails.org/latest/ref/Domain%20Classes/refresh.html&#34;&gt;Grails documentation on&lt;code&gt;refresh&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;</description>
     </item>
   
     <item>
       <title>Import Contacts Vcards Into Nextcloud</title>
       <link>https://relentlesscoding.com/posts/import-contacts-vcards-into-nextcloud/</link>
       <pubDate>Sun, 15 Oct 2017 10:37:13 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/import-contacts-vcards-into-nextcloud/</guid>
       <description>&lt;p&gt;In this post, we&amp;rsquo;re going to export our contacts from&lt;a href=&#34;https://contacts.google.com&#34;&gt;Google&lt;/a&gt; in vCard version 3 format, split thecontacts file and use &lt;code&gt;cadaver&lt;/code&gt; to upload all files individually to your addressbook.&lt;/p&gt;&lt;h2 id=&#34;the-struggle&#34;&gt;The struggle&lt;/h2&gt;&lt;p&gt;Last week, I did a fresh install of &lt;a href=&#34;https://lineageos.org/&#34;&gt;LingeageOS&lt;/a&gt; 14.1 on my &lt;a href=&#34;https://en.wikipedia.org/wiki/OnePlus_X&#34;&gt;OnePlusX&lt;/a&gt; and decided not to installany GApps. I have been slowly moving away from using Google servicesand, having found replacements in the form of open-source apps or webinterfaces, I felt confident I would be able to use my phone without aGoogle Play Store or Play Services. (F-Droid is now my sole source ofapps.)&lt;/p&gt;&lt;p&gt;To tackle the problem of storing contacts and a calendar that could besynced, I installed a&lt;a href=&#34;https://en.wikipedia.org/wiki/Nextcloud&#34;&gt;Nextcloud&lt;/a&gt; instance on aRaspberry Pi 3. Having installed&lt;a href=&#34;https://davdroid.bitfire.at/&#34;&gt;DAVdroid&lt;/a&gt;, I got my phone to synccontacts with Nextcloud, but not all of them: it would stopsynchronizing after some 120 contacts, while I had more than 400.&lt;/p&gt;&lt;p&gt;I decided to try a different approach, so I exported the contacts on myphone in &lt;a href=&#34;https://en.wikipedia.org/wiki/VCard&#34;&gt;vCard&lt;/a&gt; format and triedto upload them to Nextcloud using the aptly named application &amp;ldquo;contacts&amp;rdquo;for this. However, this also failed unexpectedly. I&amp;rsquo;m using Nextcloudversion 12.0.3 and version 2.0.1 of the contact app, but it refuses toaccept vCard version 2.1 (HTTP response code 415: Unsupported mediatype). This, naturally, is the version Android 6 uses to exportcontacts.&lt;/p&gt;&lt;p&gt;After some searching, I found out that if you go to&lt;a href=&#34;https://contacts.google.com&#34;&gt;contacts.google.com&lt;/a&gt;, you can downloadyour contacts in vCards version 3. Problem fixed? Well, not so fast:importing 400+ contacts into Nextcloud using the web interface on aRaspberry Pi 3 with an SD card for storage will take a long time. Infact, it never finished over the course of a couple of hours (!), so Ineeded yet another approach.&lt;/p&gt;&lt;p&gt;Fortunately, you can approach your Nextcloud instance through the WebDAVprotocol using tools such as&lt;a href=&#34;https://linux.die.net/man/1/cadaver&#34;&gt;&lt;code&gt;cadaver&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cadaver https://192.168.1.14/nextcloud/remote.php/dav&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Storing your credentials in a &lt;code&gt;.netrc&lt;/code&gt; file in your home directory willenable &lt;code&gt;cadaver&lt;/code&gt; to verify your identity without prompting, making itsuitable for scripting:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;machine 192.168.1.14login foopassword correcthorsebatterystaple&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;cadaver&lt;/code&gt; allows you to traverse the directories of the remote filesystem over WebDAV. To put a single local contacts file (from yourworking machine) to the remote Raspberry Pi, you could tell it to:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;dav:/nextcloud/remote.php/dav/&amp;gt; cd addressbooks/users/{username}/{addressbookname}dav:/nextcloud/remote.php/dav/addressbooks/users/foo/Contacts/&amp;gt; put /home/foo/all.vcf all.vcf&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I had a single &lt;code&gt;vcf&lt;/code&gt; file with 400+ contacts in them, but afteruploading it this way, only a single contact was being displayed.Apparently, the Nextcloud&amp;rsquo;s contacts app assumes a single &lt;code&gt;vcf&lt;/code&gt; filecontains only a single contact. New challenge: we need to split thissingle &lt;code&gt;vcf&lt;/code&gt; file containing multiple contacts into separate files thatwe can then upload to Nextcloud.&lt;/p&gt;&lt;p&gt;To split the contacts, we can use &lt;code&gt;awk&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;BEGIN {    RS=&amp;#34;END:VCARD\r?\n&amp;#34;    FS=&amp;#34;\n&amp;#34;}{    command = &amp;#34;echo -n $(pwgen 20 1).vcf&amp;#34;    command | getline filename    close(command)    print $0 &amp;#34;END:VCARD&amp;#34; &amp;gt; filename}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This separates the contacts on the record separator &lt;code&gt;END:VCARD&lt;/code&gt; andgenerates a random filename to store the individual contact in. (I also&lt;a href=&#34;https://github.com/Neftas/vcard-splitter&#34;&gt;wrote a Java program to do the samething&lt;/a&gt;, which is faster whensplitting large files).&lt;/p&gt;&lt;p&gt;Obviously, it would be convenient now if we could upload all these filesin one go. &lt;code&gt;cadaver&lt;/code&gt; does provides the &lt;code&gt;mput&lt;/code&gt; action to do so, but I didnot get it to work with wildcards. So instead, I created a file with&lt;code&gt;put&lt;/code&gt; commands:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for file in *.vcf; do    echo &amp;#34;put $(pwd)/$file addressbooks/users/foo/Contacts/$file&amp;#34; &amp;gt;&amp;gt; commandsdone&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And then provided this as input to cadaver:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cadaver http://192.168.1.14/nextcloud/remote.php/dav &amp;lt;&amp;lt;&amp;lt; $(cat commands)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This may take a while (it took around an hour for 400+ contacts), but atleast you get to see the progress as each request is made and processed.And voilà, all the contacts are displayed correctly in Nextcloud.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Always on Vpn and Captive Portals</title>
       <link>https://relentlesscoding.com/posts/always-on-vpn-and-captive-portals/</link>
       <pubDate>Tue, 12 Sep 2017 20:48:10 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/always-on-vpn-and-captive-portals/</guid>
       <description>&lt;p&gt;I was attending a meetup that was taking place in a bar in Utrecht. Thefirst thing you want to do is to make a connection to the internet andget started. The location used a &lt;a href=&#34;https://en.wikipedia.org/wiki/Captive_portal&#34;&gt;captiveportal&lt;/a&gt;, however. Youknow: you have the name of the wireless network (SSID) and the password,but when you try to open any web page, you are directed towards a loginpage where you have to accept the terms and conditions of whoever isoperating the network.&lt;/p&gt;&lt;p&gt;But what if you use an always-on VPN? You cannot connect to the network,because your MAC and IP address are not whitelisted yet by the operator.And you cannot get to the login page, because you do not allow anytraffic outside your VPN.&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/captive_portal_terms_conditions.png&#34; alt=&#34;The captive portalpage.&#34; title=&#34;The captive portal page.&#34;&gt;&lt;/p&gt;&lt;h2 id=&#34;ufw&#34;&gt;UFW&lt;/h2&gt;&lt;p&gt;I use &lt;code&gt;ufw&lt;/code&gt; (uncomplicated firewall) as my firewall of choice, mainlybecause the alternative, &lt;code&gt;iptables&lt;/code&gt;, always looked too complicated and&lt;code&gt;ufw&lt;/code&gt; served its purpose. The rules I have for &lt;code&gt;ufw&lt;/code&gt; are currently:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# ufw status verboseStatus: activeLogging: on (low)Default: deny (incoming), deny (outgoing), disabled (routed)New profiles: skipTo                         Action      From--                         ------      ----Anywhere on wlp1s0         ALLOW IN    192.168.178.0/24Anywhere                   ALLOW OUT   Anywhere on tun0-unrooted192.168.178.0/24           ALLOW OUT   Anywhere on wlp1s01194                       ALLOW OUT   Anywhere on wlp1s0Anywhere (v6)              ALLOW OUT   Anywhere (v6) on tun0-unrooted1194 (v6)                  ALLOW OUT   Anywhere (v6) on wlp1s0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By default, I deny all incoming and outgoing connections. I only allowincoming connections from hosts on the same network. As for outgoingconnections, I only allow them to other hosts over the LAN (to anyport), and everywhere else only over port 1194 (the OpenVPN port).(&lt;code&gt;wlp1s0&lt;/code&gt; is the name of my wireless interface, &lt;code&gt;tun0-unrooted&lt;/code&gt; of theVPN tunnel. These rules were inspired by &lt;a href=&#34;https://wiki.archlinux.org/index.php/OpenVPN#ufw_2&#34;&gt;this Arch Linuxarticle&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;So we need to allow some traffic outside of the VPN tunnel to accept theterms and conditions and to register our machine at the captive portal.The best thing to do would be to allow a single, trusted application toaccess this portal, one that would be used exclusively for this task. Ifyou would allow you regular browser to bypass the VPN, it would send allkind of traffic over the untrusted network for the rest of the world tofreely sniff around in (think add-ons, other browser tabs, automaticupdates). So we would need a dedicated web browser for this task. I’m onLinux using Firefox as my default browser, so &lt;a href=&#34;https://en.wikipedia.org/wiki/GNOME_Web&#34;&gt;GNOMEWeb&lt;/a&gt; would be a good choice forthis purpose. (Gnome Web was previously known as Epiphany, and is stillavailable under that name on a lot of distributions) .&lt;/p&gt;&lt;p&gt;First, we need to determine what kind of traffic we want to allow. Theapplication will need to have outbound access to ports 80 (HTTP) and 443(HTTPS) for web traffic, and it will also need to be able to resolvedomain names using DNS, so port 53 should also be opened.&lt;/p&gt;&lt;h2 id=&#34;ufw-profiles&#34;&gt;UFW Profiles&lt;/h2&gt;&lt;p&gt;However, it’s not that easy to allow one particular application accessto the internet if you use UFW. When you look at the man page for UFW,you see you can specify “apps”. Apps (or application profiles) arebasically just text files in INI-format that live in the&lt;code&gt;/etc/ufw/applications.d/&lt;/code&gt; folder.&lt;/p&gt;&lt;p&gt;To list all (predefined) profiles:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# ufw app list&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To create a profile for our purposes, we put the following in a filecalled &lt;code&gt;ufw-webbrowser&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[Epiphany]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;Epiphany&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;Epiphany web browser&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;80/tcp|443/tcp|53&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The “ports” field is clarified in the man page:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The ‘ports’ field may specify a ‘|’-separated list of ports/protocolswhere the protocol is optional. A comma-separated list or a range(specified with ‘start:end’) may also be used to specify multipleports, in which case the protocol is required.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;In our case we allow TCP traffic over ports 80 (HTTP) and 443 (HTTPS)and both UDP and TCP traffic over 53 (DNS). We can now use this profile:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# ufw insert 1 allow out to any app EpiphanyRule addedRule added (v6)# ufw status verbose[...snip...]To                         Action      From--                         ------      ----Anywhere on wlp1s0         ALLOW IN    192.168.178.0/2480/tcp (Epiphany)          ALLOW OUT   Anywhere443/tcp (Epiphany)         ALLOW OUT   Anywhere53 (Epiphany)              ALLOW OUT   AnywhereAnywhere                   ALLOW OUT   Anywhere on tun0-unrooted192.168.178.0/24           ALLOW OUT   Anywhere on wlp1s01194                       ALLOW OUT   Anywhere on wlp1s080/tcp (Epiphany (v6))     ALLOW OUT   Anywhere (v6)443/tcp (Epiphany (v6))    ALLOW OUT   Anywhere (v6)53 (Epiphany (v6))         ALLOW OUT   Anywhere (v6)Anywhere (v6)              ALLOW OUT   Anywhere (v6) on tun0-unrooted1194 (v6)                  ALLOW OUT   Anywhere (v6) on wlp1s0# ufw reload  # don&amp;#39;t forget to reload firewall after making changes!Firewall reloaded&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(Note that we use &lt;code&gt;insert 1&lt;/code&gt; to make sure the rule is placed in thefirst position. With UFW, the first rule matched wins.)&lt;/p&gt;&lt;p&gt;Now we can use our dedicated browser to go to the captive portal pageand accept the terms and conditions.&lt;/p&gt;&lt;h2 id=&#34;checking-traffic-with-wireshark&#34;&gt;Checking traffic with &lt;code&gt;wireshark&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;You need to be careful, however, not to use any other applicationsduring this time. If you launch Firefox, for example, it can also usethe opened ports to communicate with the outside world. I’d like to useWireshark to see what communications are taking place during this time.&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/wireshark_bypass_vpn-1024x563.png&#34; alt=&#34;Use wireshark to see what packets are send in theopen.&#34; title=&#34;Use wireshark to see what packets are send in the open.&#34;&gt;&lt;/p&gt;&lt;p&gt;When you are registered with the WiFi provider and are done with thecaptive portal, you should first disable the profile again with UFW. Wecan do this by specifying the rule we added earlier, but prepending&lt;code&gt;delete&lt;/code&gt;:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# ufw delete allow out to any app Epiphany&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Another way to delete rules from UFW is by first doing &lt;code&gt;ufw status numbered&lt;/code&gt; and then &lt;code&gt;ufw delete &amp;lt;number&amp;gt;&lt;/code&gt;. However, since we have added 6rules, this may take a while. Also, if you can’t remember the exact rulethat was used, you can use &lt;code&gt;ufw show added&lt;/code&gt; to show all added rules andtheir syntax.&lt;/p&gt;&lt;h2 id=&#34;better-solutions-beyond-ufw&#34;&gt;Better solutions: beyond UFW&lt;/h2&gt;&lt;p&gt;Now, we see that using UFW isn’t exactly ideal to deal with always-onVPN and captive portals. What if you have an email application (orsomething else) running in the background when you have allowed allthose ports to bypass the VPN tunnel? And also, you have to enable anddisable the application profile every time you encounter a captiveportal. It would be better if we could allow only a single, named,demarcated application to bypass the VPN.&lt;/p&gt;&lt;p&gt;One solution I’ve read about makes use of what I like to call “theAndroid way”: every installed application is a user with its own homedirectory. This means that applications don’t have access to each otherfiles, but more importantly, this gives the opportunity to allow only aspecific user to access the internet outside of the VPN. This way, wecould create a user &lt;code&gt;epiphany&lt;/code&gt; that runs Gnome Web to access the captiveportal.&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://github.com/ukanth/afwall&#34;&gt;AFWall+&lt;/a&gt;, an open-source Androidapplication, uses this method to implement a pretty effective firewall.It also uses &lt;code&gt;iptables&lt;/code&gt; as a back-end. I might have to finally bite thebullet and learn &lt;code&gt;iptables&lt;/code&gt; after all…&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Tutorial Rapid GUI Development With Qt Designer and PyQt</title>
       <link>https://relentlesscoding.com/posts/tutorial-rapid-gui-development-with-qt-designer-and-pyqt/</link>
       <pubDate>Fri, 25 Aug 2017 10:02:43 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/tutorial-rapid-gui-development-with-qt-designer-and-pyqt/</guid>
       <description>&lt;p&gt;Confession: I am the opposite of the lazy coder. I like doing things thehard way. Whether it’s developing Java in Vim without code completion,running JUnit tests on the command line (don’t forget to specify all 42dependencies in the colon-separated classpath!), creating a LaTeX graphthat looks “just right”, or writing &lt;code&gt;sqlplus&lt;/code&gt; scripts instead of usingSQL Developer (GUIs are for amateurs), I always assumed that doing sowould make me a better programmer.&lt;/p&gt;&lt;p&gt;So when I was just a fledgling programmer, and I had to design andcreate some dialog windows for a side project of mine (an &lt;a href=&#34;https://github.com/Neftas/supplementary-buttons-anki&#34;&gt;awesomeadd-on&lt;/a&gt; to&lt;a href=&#34;https://apps.ankiweb.net/&#34;&gt;AnkiSRS&lt;/a&gt; written in Python and Qt), I didwhat I always do: find &lt;a href=&#34;http://zetcode.com/gui/pyqt4/&#34;&gt;a good resource to learnPyQt&lt;/a&gt; and then code everything by hand.Now, since Python is a dynamic language and I used to develop thisadd-on in Vim, I had no code completion whatsoever, and only the Qtdocumentation and that tutorial to go by. Making things even moreinteresting, is that the documentation on Qt is in C++, and is full ofstuff like this:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/qt_documentation.png&#34; alt=&#34;Qt documentation with very readable C++ code for the Pythonnovice.&#34; title=&#34;Qt documentation in C++.&#34;&gt;&lt;/p&gt;&lt;p&gt;Obviously, I had no idea what all the asterisks and ampersands meant andhad to use good-old trial and error to see what would stick in Python.Now, on the plus side, I experimented quite a lot with the code, but nothaving code completion makes it really hard to learn the API. (And evennow, using PyCharm, code completion will not always work, because ofPython being a dynamically-typed language and all. I am still looking atthe Qt documentation quite a bit.)&lt;/p&gt;&lt;p&gt;One thing I noticed, though, is that the guy who develops Anki, &lt;a href=&#34;https://github.com/dae&#34;&gt;DamienElmes&lt;/a&gt;, had all these &lt;code&gt;.ui&lt;/code&gt; files lying around,and a bunch more files that read: “WARNING! All changes made to thisfile will be lost!”. Ugh, generated code! None of the dedicated,soul-cleansing and honest hard work that will shape you as a softwaredeveloper. It goes without saying I stayed far away from that kind oflaziness.&lt;/p&gt;&lt;p&gt;It took me some time to come around on this. One day, I actually got ajob as a professional software developer and I had much less time towork on my side-projects. So, when I wanted to implement another featurefor my Anki add-on, I found I had too little time to do it theold-fashioned way, and decided to give Qt Designer a go. Surprisingly, Ifound out it can actually help you tremendously to learn how Qt works(and also save you a bunch of time). Qt Designer allows you to visuallycreate windows using a drag-and-drop interface, then spews out an XMLrepresentation of that GUI, which can be converted to code. Thatgenerated code will show you possibilities that would have taken a longtime and a lot of StackOverflowing to figure out on your own.&lt;/p&gt;&lt;p&gt;So, to help other people discover the wonders of this program, here is alittle tutorial on how to do RAD and how to get a dialog window up andrunning with Qt Designer 4 and Python 3.&lt;/p&gt;&lt;h2 id=&#34;installation&#34;&gt;Installation&lt;/h2&gt;&lt;p&gt;First, we need to install Qt Designer. On Arch Linux, it’s part of the&lt;code&gt;qt4&lt;/code&gt; package and you’ll also need &lt;code&gt;python-pyqt4&lt;/code&gt; for the Pythonbindings. (On Ubuntu, you have to install both &lt;code&gt;qt4-designer&lt;/code&gt; and the&lt;code&gt;python-qt4&lt;/code&gt; packages.)&lt;/p&gt;&lt;h2 id=&#34;our-goal&#34;&gt;Our goal&lt;/h2&gt;&lt;p&gt;Our goal is to create a simple dialog window that has a text input fieldwhere we can type our name, and have a label that will display “Hellothere, $userName”. Yes, things will be &lt;em&gt;that&lt;/em&gt; exciting. Along the way,we will learn how to assign emitted signals to slots and how to handleevents.&lt;/p&gt;&lt;h2 id=&#34;creating-a-dialog&#34;&gt;Creating a dialog&lt;/h2&gt;&lt;p&gt;Fire up Qt Designer, and you will be presented with a “New form” dialog(if you do not see it, go to &lt;em&gt;File &amp;gt; New…&lt;/em&gt;).&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/create_new_dialog.png&#34; alt=&#34;Qt Designer’s “new form”dialog.&#34; title=&#34;Qt Designer&#39;s &amp;quot;new form&amp;quot; dialog.&#34;&gt;&lt;/p&gt;&lt;p&gt;For this tutorial, we are going to choose a fairly small “Dialog withButtons Bottom”:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/virgin_dialog_main_window.png&#34; alt=&#34;The main window of QtDesigner.&#34; title=&#34;The main window of Qt Designer.&#34;&gt;&lt;/p&gt;&lt;p&gt;To the left are the widgets that we can add to our freshly createddialog, to the right, from top to bottom, we see the currently addedwidgets, the properties of those widgets and the signals and slotscurrently assigned to the dialog window.&lt;/p&gt;&lt;p&gt;I’m going to add a text label and a so-called line editor to our widget.To make sure they will align nicely, I will put them together in ahorizontal container, a &lt;code&gt;QHBoxLayout&lt;/code&gt;:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/hbox_label_lineedit.png&#34; alt=&#34;A horizontal box layout with a text label and a lineeditor.&#34; title=&#34;A horizontal box layout with a text label and a line editor.&#34;&gt;&lt;/p&gt;&lt;p&gt;In the object inspector, we can see the hierarchy of added widgets:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/object_inspector_hbox_label_lineedit.png&#34; alt=&#34;The object-inspectorview&#34; title=&#34;The object-inspector view.&#34;&gt;&lt;/p&gt;&lt;p&gt;This is all simple drag-and-drop: we select a widget from the left pane,drag it to the desired location in the dialog and release the mouse.&lt;/p&gt;&lt;p&gt;Finally, we add two more label: one at the top, instructing the userwhat to do, and a second one near the bottom, where we soon will displayour message.&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/two_more_labels_result.png&#34; alt=&#34;Two more text labels.&#34; title=&#34;We added two moretext labels.&#34;&gt;&lt;/p&gt;&lt;p&gt;Qt Designer provides an easy way to connect signals to slots. If you goto &lt;em&gt;Edit &amp;gt; Edit Signals/Slots&lt;/em&gt; (or press F4) you will be presented witha graphical overview of the currently assigned signals and slots. Whenwe start out, the button box at the bottom already emits two signals:&lt;code&gt;rejected&lt;/code&gt; and &lt;code&gt;accepted&lt;/code&gt;, from the Cancel and Ok button respectively:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/slot_signals_buttonbox.png&#34; alt=&#34;The signals and slots of the buttonbox.&#34; title=&#34;The signals and slots of the button box.&#34;&gt;&lt;/p&gt;&lt;p&gt;The signals &lt;code&gt;rejected()&lt;/code&gt; and &lt;code&gt;accepted()&lt;/code&gt; are emitted when the canceland OK buttons are clicked respectively, and the ground symbols indicatethe object that is interested in these signals: in this case the dialogwindow itself. Signals are handled by slots, and so the &lt;code&gt;QDialog&lt;/code&gt; willneed to have slots for these signals. The slots (or handlers) are named&lt;code&gt;reject()&lt;/code&gt; and &lt;code&gt;accept()&lt;/code&gt; in this instance, and since they are defaultslots provided by Qt, they already exist in &lt;code&gt;QDialog&lt;/code&gt;, so we won’t haveto do anything (except if we want to override their default behavior).&lt;/p&gt;&lt;p&gt;What we want to do now is “catch” the &lt;code&gt;textEdited&lt;/code&gt; signal from the lineeditor widget and create a slot in the dialog window that will handleit. This new slot we’ll call &lt;code&gt;say_hello&lt;/code&gt;. So we click the line editorwidget and drag a line to anywhere on the dialog window: a ground symbolshould be visible:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/assigning_singal_to_slot.png&#34; alt=&#34;Assigning a signal to aslot&#34; title=&#34;Assigning a signal to a slot.&#34;&gt;&lt;/p&gt;&lt;p&gt;In the window that appears now, we can select the signal that we areinterested in (&lt;code&gt;textEdited&lt;/code&gt;) and assign it to a predefined slot, or wecan click on &lt;em&gt;Edit…&lt;/em&gt; and create our own slot. Let’s do that:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/create_new_slot.png&#34; alt=&#34;Creating a newslot.&#34; title=&#34;Creating a new slot.&#34;&gt;&lt;/p&gt;&lt;p&gt;We click the green plus sign, and type in the name of our new slot(&lt;code&gt;say_hello&lt;/code&gt;):&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/selecting_newly_created_slot.png&#34; alt=&#34;Selecting the newly createdslot.&#34; title=&#34;Selecting the newly created slot.&#34;&gt;&lt;/p&gt;&lt;p&gt;The result will now look like:&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/all_signal_slots_nicely_ordered.png&#34; alt=&#34;All signals andslots.&#34; title=&#34;Our custom slot in the dialog window.&#34;&gt;&lt;/p&gt;&lt;p&gt;In Qt Designer, you can preview your creation by going to &lt;em&gt;Form &amp;gt;Preview…&lt;/em&gt; (or pressing Ctrl + R). Notice, however, that typing text inthe line editor won’t do anything yet. This is because we haven’twritten any implementation code for it. In the next section we will seehow to do that.&lt;/p&gt;&lt;p&gt;You can also see the code that will be generated by going to &lt;em&gt;Form &amp;gt;View code…&lt;/em&gt;, although this code is going to be in C++.&lt;/p&gt;&lt;p&gt;Okay, enough with designing our dialog window, let’s get to actualPython coding.&lt;/p&gt;&lt;h2 id=&#34;generating-code-or-not&#34;&gt;Generating code (or not)&lt;/h2&gt;&lt;p&gt;When we save our project in Qt Designer, it will create a &lt;code&gt;.ui&lt;/code&gt; file,which is just XML containing all the properties (widgets, sizes, signals&amp;amp; slots, etc.) that make up the GUI.&lt;/p&gt;&lt;p&gt;PyQt comes with a program, &lt;code&gt;pyuic4&lt;/code&gt;, that can convert these &lt;code&gt;.ui&lt;/code&gt; filesto Python code. Two interesting command-line options are &lt;code&gt;--preview&lt;/code&gt; (or&lt;code&gt;-p&lt;/code&gt; for short), that will allow you to preview the dynamically createdGUI, and &lt;code&gt;--execute&lt;/code&gt; (or &lt;code&gt;-x&lt;/code&gt; for short), that will generate Python codethat can be executed as a stand-alone. The &lt;code&gt;--output&lt;/code&gt; switch (or &lt;code&gt;-o&lt;/code&gt;)allows you to specify a filename where the code will be saved.&lt;/p&gt;&lt;p&gt;In our case, creating a stand-alone executable is not going to work,because we have added a custom slot (&lt;code&gt;say_hello&lt;/code&gt;) that needs to beimplemented first. So we will use &lt;code&gt;pyuic4&lt;/code&gt; to generate the form class:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; pyuic4 relentless_dialog.ui -o relentless_dialog.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the result:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# -*- coding: utf-8 -*-&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Form implementation generated from reading ui file &amp;#39;relentless_dialog.ui&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Created by: PyQt4 UI code generator 4.12.1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# WARNING! All changes made in this file will be lost!&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;PyQt4&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;_fromUtf8&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QString&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fromUtf8&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;AttributeError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;_fromUtf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;_encoding&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QApplication&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;UnicodeUTF8&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;_translate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;disambig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QApplication&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;translate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;disambig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_encoding&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;AttributeError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;_translate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;disambig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QApplication&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;translate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;disambig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Ui_Dialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setupUi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setObjectName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_fromUtf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Dialog&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;resize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;250&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;320&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buttonBox&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QDialogButtonBox&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buttonBox&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setGeometry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QRect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;270&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;221&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;41&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buttonBox&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setOrientation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Qt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Horizontal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buttonBox&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setStandardButtons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QDialogButtonBox&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Cancel&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QDialogButtonBox&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buttonBox&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setObjectName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_fromUtf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;buttonBox&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;horizontalLayoutWidget&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QWidget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;horizontalLayoutWidget&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setGeometry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QRect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;40&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;231&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;80&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;horizontalLayoutWidget&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setObjectName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;_fromUtf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;horizontalLayoutWidget&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;horizontalLayout&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QHBoxLayout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;horizontalLayoutWidget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;horizontalLayout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setMargin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;horizontalLayout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setObjectName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_fromUtf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;horizontalLayout&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;label&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QLabel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;horizontalLayoutWidget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;label&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setObjectName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_fromUtf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;horizontalLayout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;addWidget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;label&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lineEdit&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QLineEdit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;horizontalLayoutWidget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lineEdit&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setObjectName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_fromUtf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;lineEdit&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;horizontalLayout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;addWidget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lineEdit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;label_2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QLabel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;label_2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setGeometry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QRect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;251&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;label_2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setObjectName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_fromUtf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;label_2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;label_3&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QLabel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;label_3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setGeometry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QRect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;190&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;241&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;label_3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setObjectName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_fromUtf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;label_3&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;retranslateUi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QObject&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;connect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buttonBox&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                               &lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SIGNAL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_fromUtf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;accepted()&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)),&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                               &lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;accept&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QObject&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;connect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buttonBox&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                               &lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SIGNAL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_fromUtf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;rejected()&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)),&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                               &lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QObject&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;connect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lineEdit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SIGNAL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_fromUtf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;textChanged(QString)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)),&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;say_hello&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QMetaObject&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;connectSlotsByName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;retranslateUi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setWindowTitle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_translate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Dialog&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Dialog&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;label&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setText&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_translate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Dialog&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Name:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;label_2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setText&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_translate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             &lt;span class=&#34;s2&#34;&gt;&amp;#34;Dialog&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p align=&amp;#34;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;center&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             &lt;span class=&#34;s2&#34;&gt;&amp;#34;Please enter your name&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;label_3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setText&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_translate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             &lt;span class=&#34;s2&#34;&gt;&amp;#34;Dialog&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;lt;html&amp;gt;&amp;lt;head/&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p align=&amp;#34;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;center&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             &lt;span class=&#34;s2&#34;&gt;&amp;#34;[Where our message will appear]&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It is here that you can learn a lot about how PyQt works, even thoughsome statements seem a bit baroque, like the binding of the&lt;code&gt;textChanged&lt;/code&gt; signal to our custom slot:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QObject&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;connect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lineEdit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;QtCore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SIGNAL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_fromUtf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;textChanged(QString)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)),&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;say_hello&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you would write this yourself, you would probably prefer:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lineEdit&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;textChanged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;connect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Dialog&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;say_hello&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(Incidentally, the text between the square brackets in&lt;code&gt;textChanged[str]&lt;/code&gt; indicates that a single argument of this type ispassed to the slot.)&lt;/p&gt;&lt;h2 id=&#34;bringing-it-all-together&#34;&gt;Bringing it all together&lt;/h2&gt;&lt;p&gt;Having generated our form class, we can now create a base class thatwill use this class:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sys&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;PyQt4&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;relentless_dialog&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Ui_Dialog&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;MyDialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QDialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MyDialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ui&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Ui_Dialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setupUi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;show&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;say_hello&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;user_text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Hello there, &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{0}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;!&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user_text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;label_3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setText&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QApplication&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;argv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dialog&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MyDialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;exec_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When we are passing &lt;code&gt;self&lt;/code&gt; to &lt;code&gt;self.ui.setupUi&lt;/code&gt;, we are passing thewidget (a &lt;code&gt;QDialog&lt;/code&gt;) in which the user interface will be created.&lt;/p&gt;&lt;p&gt;We implement our custom slot by defining a method &lt;code&gt;say_hello&lt;/code&gt; that takesa single argument (the user-provided text from the line editor). Wecraft a witty sentence with it, and set it as the label text.&lt;/p&gt;&lt;p&gt;As mentioned before, we could also dynamically create the GUI directlyfrom the &lt;code&gt;.ui&lt;/code&gt; file:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sys&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;PyQt4&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;PyQt4&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uic&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;MyDialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QtGui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QDialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MyDialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;dialog&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uic&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loadUi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;relentless_dialog.ui&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;dialog&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;show&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# etc&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;running-the-application&#34;&gt;Running the application&lt;/h2&gt;&lt;p&gt;Running this will show the following (drum roll):&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://relentlesscoding.com/img/end_result.png&#34; alt=&#34;Running theapplication.&#34; title=&#34;Running the application.&#34;&gt;&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>How to Use Groovy&#39;s CliBuilder</title>
       <link>https://relentlesscoding.com/posts/how-to-use-groovys-clibuilder/</link>
       <pubDate>Fri, 11 Aug 2017 16:35:57 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/how-to-use-groovys-clibuilder/</guid>
       <description>&lt;p&gt;Let&amp;rsquo;s use Groovy&amp;rsquo;s &lt;code&gt;CliBuilder&lt;/code&gt; to create CLI programs that can take flags.&lt;/p&gt;&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;&lt;p&gt;I write quite some scripts at work, either for myself or for my team. Iused to write these scripts in Bash, but since we are using Windows atwork, it made sense to switch to a more general scripting language.Because we’re a Java shop, Groovy seemed like a natural choice. Now,it’s often very convenient to have named command-line arguments (e.g.&lt;code&gt;tool --output out.ext --input in.ext&lt;/code&gt;) as opposed to positionalarguments (e.g. &lt;code&gt;tool out.ext in.ext&lt;/code&gt;): named arguments allow forputting the arguments in any order you want, and you can leave outoptional arguments and use the defaults. Groovy’s &lt;code&gt;CliBuilder&lt;/code&gt; makesthings especially easy.&lt;/p&gt;&lt;h2 id=&#34;a-sample-program&#34;&gt;A sample program&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;weather.groovy&lt;/strong&gt;&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Grab&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;group&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;commons-cli&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;module&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;commons-cli&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;1.4&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Main&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;CommandLineInterface&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cli&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CommandLineInterface&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;INSTANCE&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;cli&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;CommandLineInterface&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;INSTANCE&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;CliBuilder&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cliBuilder&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;CommandLineInterface&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;cliBuilder&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CliBuilder&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nl&#34;&gt;usage:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;weather [&amp;lt;options&amp;gt;]&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nl&#34;&gt;header:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Options:&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nl&#34;&gt;footer:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;And here we put footer text.&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// set the amount of columns the usage message will be wide&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;cliBuilder&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;width&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;80&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// default is 74&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;cliBuilder&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;h&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;longOpt:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;help&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Print this help text and exit.&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;longOpt:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;max-count&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;args:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;argName:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;number&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;s1&#34;&gt;&amp;#39;Limit the number of days shown&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;longOpt:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;one&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Show today&amp;#39;s forecast&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;2&amp;#39;&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;longOpt:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;two&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Show today&amp;#39;s and tomorrow&amp;#39;s forecast&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;longOpt:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;args:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;argName:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;URL&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;s1&#34;&gt;&amp;#39;Use given URL to query for weather.&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;args:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;valueSeparator:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;=&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;argName:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;property=value&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;s1&#34;&gt;&amp;#39;Use value for given property.&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;OptionAccessor&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;options&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cliBuilder&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Error while parsing command-line options.\n&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;cliBuilder&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;usage&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// handle user&amp;#39;s input&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// show weather for one day&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;URI&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uri&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;URI&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Ds&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;assert&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Ds&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ArrayList&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When invoked with &lt;code&gt;groovy weather.groovy --help&lt;/code&gt;, the program willoutput:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;usage: weather []&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Options:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt; -1,--one           Show today&amp;#39;s forecast&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt; -2,--two           Show today&amp;#39;s and tomorrow&amp;#39;s forecast&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt; -D                 Use value for given property.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt; -h,--help          Print this help text and exit.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt; -n,--max-count     Limit the number of days shown&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;    --url           Use given URL to query for weather.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;And here can put footer text.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can specify short and long names: &lt;code&gt;cliBuilder.n&lt;/code&gt; specifies that theprogram will accept an argument &lt;code&gt;-n&lt;/code&gt;. To also specify a long name, weadd &lt;code&gt;longOpt: max-count&lt;/code&gt;. To use only a long name, we can replace theshort name by &lt;code&gt;_&lt;/code&gt;: &lt;code&gt;cliBuilder._(longName: &#39;wow-such-long&#39;)&lt;/code&gt;. Note that&lt;code&gt;cliBuilder._&lt;/code&gt; can be reused several times (in contrast to other shortnames).&lt;/p&gt;&lt;p&gt;The number of arguments can be indicated by using the &lt;code&gt;args&lt;/code&gt; parameter.If you use more than one parameter, a &lt;code&gt;valueSeparator&lt;/code&gt; must beindicated, which is used to split up the argument. I found that thenumber assigned to &lt;code&gt;args&lt;/code&gt; is not very strict: when called with argument&lt;code&gt;-Dmyprop&lt;/code&gt;, the following is valid.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;assert&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Ds&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;myprop&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note how we append an &lt;code&gt;s&lt;/code&gt; to the option name (&lt;code&gt;options.Ds&lt;/code&gt;) to get alist of properties and values. A normal invocation &lt;code&gt;groovy weather.groovy -Dpancakes=yesplease&lt;/code&gt; would result in:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;assert&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Ds&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;pancakes&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;yesplease&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When &lt;code&gt;required: true&lt;/code&gt; is set, the program will shout at you if theargument is not used. For example, if we specify &lt;code&gt;cliBuilder.q(required: true)&lt;/code&gt; but fail to provide the &lt;code&gt;-q&lt;/code&gt; argument on the command line, theprogram will exit with &lt;code&gt;error: Missing required option: q&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;One thing to remember is that, if you want to use digits as argumentnames, you have to stringify them first by putting quotation marksaround them: &lt;code&gt;cliBuilder.&#39;1&#39;&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;more-on-clibuilder&#34;&gt;More on CliBuilder&lt;/h2&gt;&lt;p&gt;Check the &lt;a href=&#34;http://docs.groovy-lang.org/latest/html/gapi/groovy/util/CliBuilder.html&#34;&gt;Groovy documentation onCliBuilder&lt;/a&gt;for more information.&lt;/p&gt;&lt;h2 id=&#34;dependencies&#34;&gt;Dependencies&lt;/h2&gt;&lt;p&gt;Groovy’s &lt;code&gt;CliBuilder&lt;/code&gt; depends on &lt;code&gt;cli-commons&lt;/code&gt;:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;build.gradle&lt;/strong&gt;&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gradle&#34; data-lang=&#34;gradle&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;dependencies&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;compile&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;group:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;commons-cli&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;name:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;commons-cli&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;version:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;1.4&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Using Groovy&#39;s AntBuilder to Zip and Unzip Files</title>
       <link>https://relentlesscoding.com/posts/using-groovys-antbuilder-to-zip-and-unzip-files/</link>
       <pubDate>Fri, 04 Aug 2017 12:52:20 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/using-groovys-antbuilder-to-zip-and-unzip-files/</guid>
       <description>&lt;p&gt;Need to zip or unzip files? Let&amp;rsquo;s take a look at how Groovy solves this.&lt;/p&gt;&lt;p&gt;Suppose we need to zip a bunch of Python files (without their compiledcounterparts &lt;code&gt;*.pyc&lt;/code&gt;). The next (admittedly contrived) example shows howwe could go about doing something like this in Groovy:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;File&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;srcDir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/home/neftas/Downloads/scripts/&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;File&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;destDir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;srcDir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;antexample&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;File&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;zippedFile&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;srcDir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;antzipped.zip&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;allPythonFilenames&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;srcDir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;findAll&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;endsWith&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;.py&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;assert&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;a.py&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;b.py&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;c.py&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;allPythonFilenames&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ant&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AntBuilder&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ant&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;begin zipping and unzipping&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;zip&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;destfile:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;zippedFile&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;basedir:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;srcDir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;includes:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.py&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mkdir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;dir:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;destDir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;unzip&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;src:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;zippedFile&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;dest:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;destDir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;done zipping and unzipping&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// zip file is created&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;assert&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;zippedFile&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;exists&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// contents of zip file should match content of source directory&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;commands&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;bash&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;-c&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;zipinfo -1 ${zippedFile.name}&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;process&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;commands&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;execute&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;srcDir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;contentsOfZip&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;lineSeparator&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;assert&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;contentsOfZip&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;allPythonFilenames&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// all files should be unpacked from the zip into the right directory&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;assert&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;destDir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;allPythonFilenames&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ant&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;deleting created files&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// notice nested Ant task&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;delete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;includeEmptyDirs:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;fileset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;dir:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;destDir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;fileset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;file:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;zippedFile&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;deleted created files&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can use all the power of Ant in our Groovy scripts. Ant task namesmap to Groovy methods (see the &lt;code&gt;zip&lt;/code&gt; method above) and the attributesare passed as maps to these methods. What&amp;rsquo;s even better, there is noneed to stringify the arguments of these attributes (as is required inAnt&amp;rsquo;s XML), but we can directly use the correct datatypes (e.g. in thekey-value pair &lt;code&gt;destfile: zippedFile&lt;/code&gt;, &lt;code&gt;zippedFile&lt;/code&gt; is of type&lt;code&gt;java.util.File&lt;/code&gt;). All attributes of Ant tasks are available to use with&lt;code&gt;AntBuilder&lt;/code&gt; (see &lt;a href=&#34;https://ant.apache.org/manual/Tasks/zip.html&#34;&gt;Ant manual for ziptask&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;To use nested Ant tasks, we create closures, so that Ant&amp;rsquo;s:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;delete&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;includeEmptyDirs=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;fileset&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;dir=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;lib&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;fileset&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;file=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;file.ext&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/delete&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;results in the following Groovy code:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ant&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;delete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;includeEmptyDirs:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;fileset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;dir:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;lib&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;fileset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;file:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;file.ext&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;invoking-from-gradle&#34;&gt;Invoking from Gradle&lt;/h2&gt;&lt;p&gt;&lt;code&gt;AntBuilder&lt;/code&gt; is also available in Gradle. This will come in handy whenyou don&amp;rsquo;t know the &amp;ldquo;Gradle way&amp;rdquo; of doing something, but do know how totackle it in Ant.&lt;/p&gt;&lt;h2 id=&#34;dependencies&#34;&gt;Dependencies&lt;/h2&gt;&lt;p&gt;Groovy (and Gradle) come with a bundled version of &lt;code&gt;AntBuilder&lt;/code&gt;, so youshould be good shape when creating a Groovy script, but if you want touse &lt;code&gt;AntBuilder&lt;/code&gt; in a project, you should add &lt;code&gt;ant&lt;/code&gt; and &lt;code&gt;ant-launcher&lt;/code&gt;to your &lt;code&gt;build.gradle&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gradle&#34; data-lang=&#34;gradle&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;dependencies&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;compile&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;group:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;ant&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;name:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;ant&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;nl&#34;&gt;version:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;1.7.0&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;compile&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;group:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;ant&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;name:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;ant-launcher&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;nl&#34;&gt;version:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;1.6.5&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>I Like My Method Pointers With Curry in Groovy</title>
       <link>https://relentlesscoding.com/posts/i-like-my-method-pointers-with-curry-in-groovy/</link>
       <pubDate>Fri, 28 Jul 2017 17:13:09 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/i-like-my-method-pointers-with-curry-in-groovy/</guid>
       <description>&lt;p&gt;Invoking a method several times can easily be done in Java in a loop.But what if we have several methods that need to be executed severaltimes? Since Java does not feature pointers to functions, we cannot passfunctions around, and cannot create a generic method that takes apointer to a method and executes it. This is where Groovy shines.&lt;/p&gt;&lt;p&gt;Suppose we want to check on the status of some external system or work.We want to check the status every half a second for a maximum of tentimes in total.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// the generic type in Closure&amp;lt;Boolean&amp;gt; indicates its return type&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;isOperationSuccessful&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;numOfTries&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Closure&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Boolean&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Thread&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;sleep&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;500&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;..&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;numOfTries&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;Thread&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;sleep&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;500&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// we can pass closures...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;check&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Random&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;nextInt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;println&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;isOperationSuccessful&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;check&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;curry&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// ... as well as methods&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;isWorkDone&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// check if particular external system identified by `id` is done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Random&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;nextInt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// `.&amp;amp;` is the method pointer operator&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;println&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;isOperationSuccessful&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isWorkDone&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;curry&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can pass methods around by referencing them with the &lt;a href=&#34;http://docs.groovy-lang.org/latest/html/documentation/core-operators.html#method-pointer-operator&#34;&gt;method pointeroperator&lt;code&gt;.&amp;amp;&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pointerToUpper&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;hello world&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;toUpperCase&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;assert&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;pointerToUpper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;HELLO WORLD&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By using &lt;a href=&#34;http://groovy-lang.org/closures.html#_functional_programming&#34;&gt;Groovy&amp;rsquo;s take oncurrying&lt;/a&gt;,we can fix parameters of a closure (or method):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;power&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;exponent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;exponent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;squared&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;power&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;rcurry&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;assert&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;squared&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;144&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;rcurry&lt;/code&gt; fixes the right-most argument of the method, &lt;code&gt;curry&lt;/code&gt; theleft-most. (And yes, there is also an index-based method: &lt;code&gt;ncurry&lt;/code&gt;).Both return a new &lt;code&gt;Closure&lt;/code&gt; that takes one less argument.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>How to Ignore an Invalid SSL Certificate in Java</title>
       <link>https://relentlesscoding.com/posts/how-to-ignore-an-invalid-ssl-certificate-in-java/</link>
       <pubDate>Mon, 24 Jul 2017 07:29:34 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/how-to-ignore-an-invalid-ssl-certificate-in-java/</guid>
       <description>&lt;p&gt;Sometimes during development it is useful to use a certificate whose CN(Common Name) does not match the host name in the URL, for example&lt;code&gt;localhost&lt;/code&gt;. In these cases Java will throw an &lt;code&gt;SSLHandshakeException&lt;/code&gt;.How can we easily disable certificate checking for &lt;code&gt;localhost&lt;/code&gt; and otherdomains of our choosing?&lt;/p&gt;&lt;h2 id=&#34;your-own-certificate-checking&#34;&gt;Your own certificate checking&lt;/h2&gt;&lt;p&gt;The easiest way is to create your own class that implements theinterface &lt;code&gt;HostnameVerifier&lt;/code&gt;:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;WhitelistHostnameVerifier.java&lt;/strong&gt;&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;com.relentlesscoding.https&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;javax.net.ssl.HostnameVerifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;javax.net.ssl.HttpsURLConnection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;javax.net.ssl.SSLSession&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;java.util.HashSet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;java.util.Set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// singleton&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;enum&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WhitelistHostnameVerifier&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HostnameVerifier&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// these hosts get whitelisted&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;INSTANCE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;localhost&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;sub.domain.com&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Set&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;whitelist&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HashSet&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HostnameVerifier&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;defaultHostnameVerifier&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpsURLConnection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getDefaultHostnameVerifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WhitelistHostnameVerifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hostnames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hostname&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hostnames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;whitelist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hostname&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;verify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SSLSession&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;session&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;whitelist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// important: use default verifier for all other hosts&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;defaultHostnameVerifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;verify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;session&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Main.java&lt;/strong&gt;&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// use our HostnameVerifier for all future connections&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpsURLConnection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setDefaultHostnameVerifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WhitelistHostnameVerifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;INSTANCE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://relentlesscoding.com&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpsURLConnection&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpsURLConnection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;URL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;openConnection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getResponseCode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;HttpsURLConnection.DefaultHostnameVerifier#verify&lt;/code&gt; always returns&lt;code&gt;false&lt;/code&gt;, so we might as well return &lt;code&gt;false&lt;/code&gt; ourselves, but this way thecode will keep working when the implementation of &lt;code&gt;HttpsURLConnection&lt;/code&gt;changes.&lt;/p&gt;&lt;p&gt;Note: you can set the default &lt;code&gt;HostnameVerifier&lt;/code&gt; globally by using thestatic method &lt;code&gt;setDefaultHostnameVerifier&lt;/code&gt;, or you can set it perinstance, using &lt;code&gt;conn.setHostnameVerifier&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;problems-with-lets-encrypt-certificates&#34;&gt;Problems with Let’s Encrypt certificates&lt;/h2&gt;&lt;p&gt;One thing I found out while testing this, was that OpenJDK version1.8.0_131 won’t correctly handle Let’s Encrypt certificates:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;java.io.IOException: HTTPS hostname wrong: should be &amp;lt;relentlesscoding.com&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is due to the fact that Let’s Encrypt certificates are not presentin the Java installation. Upgrading to version 1.8.0_141 resolves this(for Oracle versions &amp;gt;= 8U101 or &amp;gt;= 7U111).&lt;/p&gt;&lt;h2 id=&#34;whitelisting-not-disabling-certificate-checking&#34;&gt;Whitelisting, &lt;strong&gt;NOT&lt;/strong&gt; disabling certificate checking&lt;/h2&gt;&lt;p&gt;The best approach here is whitelisting. This will keep the certificatechecking in place for most sites, and will only disable it forpre-approved hosts. I saw quite a bit of examples online that disablecertificate checking completely, by writing the &lt;code&gt;verify&lt;/code&gt; method asfollows:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;verify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hostname&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SSLSession&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;session&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is &lt;em&gt;not&lt;/em&gt; secure, as it completely ignores &lt;em&gt;all&lt;/em&gt; invalidcertificates.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Creating Custom Intershop ISML Functions With CustomTag</title>
       <link>https://relentlesscoding.com/posts/creating-custom-intershop-isml-functions-with-customtag/</link>
       <pubDate>Sat, 22 Jul 2017 17:27:33 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/creating-custom-intershop-isml-functions-with-customtag/</guid>
       <description>&lt;p&gt;In this post, I&amp;rsquo;ll look at leveraging Intershop&amp;rsquo;s &lt;code&gt;CustomTag&lt;/code&gt; to create customfunctionality in ISML templates.&lt;/p&gt;&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;&lt;p&gt;I wanted to capitalize a certain value from the pipeline dictionary inan ISML template. Intershop provides ISML functions of the kind &lt;code&gt;ucase&lt;/code&gt;and &lt;code&gt;lcase&lt;/code&gt; to transform a string to uppercase or lowercaserespectively. When the value of the dictionary name &lt;code&gt;Product:Name&lt;/code&gt; is&lt;code&gt;AwesomeNotebook&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;isprint&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;#ucase(Product:Name)#&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will print &lt;code&gt;AWESOMENOTEBOOK&lt;/code&gt;. Unfortunately, a similar function tocapitalize a string is not provided by Intershop. Ideally, you wouldwant to be able to implement your own methods, so that you could type:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;isprint&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;#capitalize(Product:Name)#&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;what-about-custom-modules&#34;&gt;What about custom modules?&lt;/h2&gt;&lt;p&gt;Intershop does provide the ability to create custom modules. These arebasically just tags you can give whatever name you like, and you canprovide an implementation in Java or ISML. The interface is defined in a&lt;code&gt;modules.isml&lt;/code&gt; file. You could, for example, define a custom tag&lt;code&gt;isprintdefault&lt;/code&gt; that prints a default value whenever the &lt;code&gt;value&lt;/code&gt; passedto it is &lt;code&gt;null&lt;/code&gt; or the empty string:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;isprintdefault&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;(unknown)&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;#Product:Price&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This would print &lt;code&gt;(unknown)&lt;/code&gt; every time &lt;code&gt;Product:Price&lt;/code&gt; does not containa value.&lt;/p&gt;&lt;p&gt;This solution, however, has two drawbacks:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;For a Java implementation, this would come down to writingscriptlet. A few lines of scriptlet is fine ofcourse, but as thesize of the code increases, it becomes harder and harder tomaintain, because there is no compilation-time check and debuggingis nearly impossible (say hello to a thousand print statements).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Also, it is cumbersome for implementing something like thecapitalize function mentioned above, because you would have toprovide the input to the custom module, put the result in thepipeline dictionary, and then use the pipeline dictionary name:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;iscapitalize&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;dictname&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;capitalizedStr&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;input&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;#Product:Name#&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;isprint&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;#capitalizedStr#&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instead of just using the returned capitalized string directly:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;isprint&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;#capitalize(Product:Name)#&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;solution-isml-custom-tag&#34;&gt;Solution: ISML Custom Tag&lt;/h2&gt;&lt;p&gt;An &lt;a href=&#34;https://support.intershop.com/kb/index.php/Display/G24787&#34;&gt;ISML customtag&lt;/a&gt; looksjust like a custom module:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ISUUID&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;CategoryRenderEntityID&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(Incidently, this creates a UUID and stores it in the pipelinedictionary variable name &lt;code&gt;CategoryRenderEntityID&lt;/code&gt;). According to thedocumentation, the difference between a custom &lt;em&gt;tag&lt;/em&gt; and a custom&lt;em&gt;module&lt;/em&gt; is that the former “provides a significant performance increase”compared to the latter.&lt;/p&gt;&lt;h3 id=&#34;two-purposes&#34;&gt;Two purposes&lt;/h3&gt;&lt;p&gt;Custom tags can be used for two purposes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Performing a single operation, like we saw with the &lt;code&gt;ISUUID&lt;/code&gt; customtag above, where the tag performs a single action (creating a newUUID and putting it in the pipeline dictionary).&lt;/li&gt;&lt;li&gt;Inserting an instance of a helper or utilities class into thepipeline dictionary.&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&#34;creating-an-isml-custom-tag&#34;&gt;Creating an ISML Custom Tag&lt;/h3&gt;&lt;p&gt;Say, we want to create an ISML Custom Tag named &lt;code&gt;&amp;lt;ishelper&amp;gt;&lt;/code&gt;. First, wewould define it in a &lt;code&gt;modules.properties&lt;/code&gt; file, located at&lt;code&gt;&amp;lt;cartridge&amp;gt;/staticfiles/cartridge/config/modules.properties&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-properties&#34; data-lang=&#34;properties&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ismodules.helper.class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;custom_base.internal.modules.Helper&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ismodules.helper.isStrict&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ismodules.helper.parameters&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;alias&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ismodules.helper.parameter.alias.type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;java.lang.String&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ismodules.helper.parameter.alias.isRequired&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;class&lt;/code&gt; property should point to the implementing Java class. Sincea pipeline dictionary is available to custom tags, we can define whetherwe want a clean, strict dictionary or not, much like we can do inpipelines. Parameters and return values can be defined by providingtheir name, type and whether or not they are mandatory.&lt;/p&gt;&lt;p&gt;So, the properties file above defines a custom tag &lt;code&gt;&amp;lt;ishelper&amp;gt;&lt;/code&gt; (put aleading &lt;code&gt;is&lt;/code&gt; to your lowercase class name), that has an optionalattribute &lt;code&gt;alias&lt;/code&gt; of type &lt;code&gt;String&lt;/code&gt;. Other parameters can be added byusing comma’s:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-properties&#34; data-lang=&#34;properties&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ismodules.helper.parameters&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;alias,more,params&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Don’t forget to also add a type and whether the parameter is required ornot. The type of the parameter can be any Java class, so you’re notlimited to just strings.&lt;/p&gt;&lt;p&gt;The Java class with the implementing code must extend the abstract class&lt;code&gt;CustomTag&lt;/code&gt;. It is required to override the method &lt;code&gt;processOpenTag&lt;/code&gt;,which will be called every time the custom tag is encountered in theISML template.&lt;/p&gt;&lt;p&gt;Now then, we can create “custom ISML functions” by adding an instance ofour &lt;code&gt;Helper&lt;/code&gt; class to the pipeline dictionary:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Helper&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CustomTag&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;processOpenTag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PageContext&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;paramPageContext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                                &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ServletResponse&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;paramServletResponse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                                &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AbstractTemplate&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;paramAbstractTemplate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                                &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;paramInt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;throws&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IOException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ServletException&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PipelineDictionary&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dict&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AbstractTemplate&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getTemplateExecutionConfig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getPipelineDictionary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;alias&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dict&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getOptional&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;alias&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Helper&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dict&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;put&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;alias&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Helper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we wanted a function &lt;code&gt;capitalize&lt;/code&gt; in our templates, we could add sucha method to our Java class:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getCapitalize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;cm&#34;&gt;/* do groundbreaking stuff */&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;capitalizedStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that the method name should start with &lt;code&gt;get&lt;/code&gt; in order for it to beavailable in the pipeline dictionary in our templates. We can now usethe method as follows:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;isprint&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;#Helper:Capitalize(Product:Name)#&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Don’t forget, however, to add the custom tag somewhere at the beginningof the template before using it:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ishelper&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;alias&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;MyCustomUtilsClass&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;&amp;lt;!-- snip --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;isloop&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;iterator&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;MyProducts&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;alias&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;product&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;isprint&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;#MyCustomUtilsClass:Capitalize(product:Name)#&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;isloop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(Note that the &lt;code&gt;alias&lt;/code&gt; attribute is optional. If you leave it out, itwill default to &lt;code&gt;Helper&lt;/code&gt;: see the implementation above. This will be the&lt;em&gt;alias&lt;/em&gt; of the class we’re using to invoke methods on:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;isprint&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;#MyCustomUtilsClass:noArgMethod()#&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Remember that the name of the actual class that contains the custom tagis part of the tag name: &lt;code&gt;ishelper&lt;/code&gt;.)&lt;/p&gt;&lt;h2 id=&#34;alternative-method-of-making-a-java-class-available-in-the-pipeline-dictionary&#34;&gt;Alternative method of making a Java class available in the pipeline dictionary&lt;/h2&gt;&lt;p&gt;Obviously, we do not have to rely on an ISML custom tag to put aninstance of a Java class in the pipeline dictionary. We could justcreate a pipelet that does this, and put this pipelet in a pipeline thatgets called a lot (a &lt;code&gt;Prefix&lt;/code&gt; pipeline would be good). This would removethe need to put &lt;code&gt;&amp;lt;ishelper&amp;gt;&lt;/code&gt; (or whatever we named the custom tag) atthe top of our templates. But the helper class would only be availableif the &lt;code&gt;Prefix&lt;/code&gt; pipeline was called, so you would need to check thatcarefully. Putting the custom tag at the top of the template makes theimport explicit and easily verifiable.&lt;/p&gt;&lt;h2 id=&#34;a-word-of-caution&#34;&gt;A word of caution&lt;/h2&gt;&lt;p&gt;Custom tags are very handy, but they extend the internal &lt;code&gt;CustomTag&lt;/code&gt;class. This means Intershop does not encourage using them in customproject development. In fact, you won’t find any information about themin the documentation.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Java Stateful Sessions or How to Properly Send Cookies With Each Redirect Request</title>
       <link>https://relentlesscoding.com/posts/java-stateful-sessions-or-how-to-properly-send-cookies-with-each-redirect-request/</link>
       <pubDate>Fri, 14 Jul 2017 19:05:45 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/java-stateful-sessions-or-how-to-properly-send-cookies-with-each-redirect-request/</guid>
       <description>&lt;p&gt;Sometimes you need to log in to some webpage programmatically. I ran into one ofthose pages where, when the login succeeds, you&amp;rsquo;re being redirected to anotherpage, and then to another (something along the lines of ‘Please login’ -&amp;gt; ‘Youare successfully logged in’ -&amp;gt; ‘Admin panel’). So I needed to write somethingthat would store the cookies that come along with each response, and send thosecookies out with each subsequent request. With &lt;code&gt;curl&lt;/code&gt;, I would have used:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; curl --location --cookie-jar logincookie &lt;span class=&#34;s1&#34;&gt;&amp;#39;https://login.securepage.com&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So how can we emulate this behavior in Java?&lt;/p&gt;&lt;h2 id=&#34;java-ses-cookiehandler&#34;&gt;Java SE’s &lt;code&gt;CookieHandler&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;A simple &lt;a href=&#34;https://duckduckgo.com/?q=java+cookies&#34;&gt;DuckDuckGo search&lt;/a&gt;will show Java SE’s own&lt;a href=&#34;https://docs.oracle.com/javase/8/docs/api/java/net/CookieHandler.html&#34;&gt;&lt;code&gt;java.net.CookieHandler&lt;/code&gt;&lt;/a&gt;:pretty neat, a built-in solution! The &lt;code&gt;CookieHandler&lt;/code&gt;, as the namesuggests, will handle all your floating-point arithmetic. Just kidding,it will serve as the object where the HTTP protocol handler (used byobjects such as &lt;code&gt;URL&lt;/code&gt;) will go to to check for relevant cookies.&lt;code&gt;CookieHandler&lt;/code&gt; is a global abstract class that will handle the statemanagement of &lt;em&gt;all&lt;/em&gt; requests.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;CookieManager&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cookieManager&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CookieManager&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cookieManager&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setCookiePolicy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CookiePolicy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ACCEPT_ALL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CookieHandler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setDefault&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cookieManager&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://docs.oracle.com/javase/8/docs/api/java/net/CookieManager.html&#34;&gt;&lt;code&gt;CookieManager&lt;/code&gt;&lt;/a&gt;’stask is to decide which cookies to accept and which to reject.&lt;a href=&#34;https://docs.oracle.com/javase/8/docs/api/java/net/CookiePolicy.html#ACCEPT_ALL&#34;&gt;&lt;code&gt;CookiePolicy.ACCEPT_ALL&lt;/code&gt;&lt;/a&gt;will accept all cookies. Other choices include&lt;a href=&#34;https://docs.oracle.com/javase/8/docs/api/java/net/CookiePolicy.html#ACCEPT_NONE&#34;&gt;&lt;code&gt;CorkiePolicy.ACCEPT_NONE&lt;/code&gt;&lt;/a&gt;(no cookies will be accepted) and&lt;a href=&#34;https://docs.oracle.com/javase/8/docs/api/java/net/CookiePolicy.html#ACCEPT_ORIGINAL_SERVER&#34;&gt;&lt;code&gt;CookiePolicy.ACCEPT_ORIGINAL_SERVER&lt;/code&gt;&lt;/a&gt;(only cookies from the original server will be accepted).&lt;/p&gt;&lt;p&gt;If you want, you can also create a &lt;code&gt;CookieManager&lt;/code&gt; with a specific&lt;a href=&#34;https://docs.oracle.com/javase/8/docs/api/java/net/CookieStore.html&#34;&gt;&lt;code&gt;CookieStore&lt;/code&gt;&lt;/a&gt;and specify exactly where your cookies will be stored. This is needed,for example, to create persistent cookies that can be restored after aJVM restart. By default, &lt;code&gt;CookieManager&lt;/code&gt; will create an in-memory cookiestore.&lt;/p&gt;&lt;p&gt;To install our own system-wide cookie handler, we invoke&lt;code&gt;CookieHandler.setDefault&lt;/code&gt; and pass our &lt;code&gt;CookieManager&lt;/code&gt; as an argument.&lt;/p&gt;&lt;p&gt;The tutorial on &lt;a href=&#34;https://docs.oracle.com/javase/tutorial/networking/cookies/&#34;&gt;the Oraclewebsite&lt;/a&gt;does a pretty decent job of explaining everything in more detail.&lt;/p&gt;&lt;p&gt;If this works for you, great! If not, keep on reading.&lt;/p&gt;&lt;h2 id=&#34;apache-httpcomponent-client-to-the-rescue&#34;&gt;Apache &lt;code&gt;HttpComponent Client&lt;/code&gt; to the rescue&lt;/h2&gt;&lt;p&gt;Unfortunately, the steps above did not work for me as the server keptcomplaining that the session was invalid after the redirects. Afterreading somewhere on StackOverflow that Java SE’s version of the&lt;code&gt;CookieStore&lt;/code&gt; was buggy, and me being on a deadline, I decided to turnmy attention to Apache and their &lt;code&gt;HttpComponent Client&lt;/code&gt; (version 4.5.3):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.apache.http.client.CookieStore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.apache.http.client.config.CookieSpecs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.apache.http.client.config.RequestConfig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// ... other imports&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Downloader&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CookieStore&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cookieStore&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BasicCookieStore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;doLogin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// CookieSpecs.STANDARD is the RFC 6265 compliant policy&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RequestConfig&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;requestConfig&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RequestConfig&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;custom&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setCookieSpec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CookieSpecs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;STANDARD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// automatically follow redirects&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CloseableHttpClient&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpClients&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;custom&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setRedirectStrategy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LaxRedirectStrategy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setDefaultRequestConfig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;requestConfig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setDefaultCookieStore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cookieStore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;addr&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://login.securehost.com&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpPost&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpPost&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// login and password as payload for POST request&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;NameValuePair&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;urlParams&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ArrayList&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;urlParams&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BasicNameValuePair&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;login&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;neftas&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;urlParams&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BasicNameValuePair&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;letmein&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;UrlEncodedFormEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;urlParams&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpResponse&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;execute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// ... and we are logged in!&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(To keep the code clean, I haven&amp;rsquo;t bothered with any exception handling.)&lt;/p&gt;&lt;p&gt;The &lt;code&gt;RequestConfig&lt;/code&gt; serves to indicate what kind of cookie policy we want(&lt;a href=&#34;https://hc.apache.org/httpcomponents-client-4.5.x/tutorial/html/statemgmt.html#d5e515&#34;&gt;there areseveral&lt;/a&gt;).If you just want things to work, you can start with &lt;code&gt;CookieSpecs.STANDARD&lt;/code&gt;,which is compliant with “a more relaxed profile defined by RFC 6265”.&lt;/p&gt;&lt;p&gt;During the creation of the &lt;code&gt;HttpClient&lt;/code&gt;, we specify that we want tofollow all redirects (&lt;code&gt;setRedirectStrategy(new LaxRedirectStrategy())&lt;/code&gt;),we pass the cookie policy we defined earlier in the &lt;code&gt;RequestConfig&lt;/code&gt;, andlastly, we pass the cookie store.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Adding a File to the Root of Your Gradle Distribution</title>
       <link>https://relentlesscoding.com/posts/adding-a-file-to-the-root-of-your-gradle-distribution/</link>
       <pubDate>Sat, 08 Jul 2017 20:21:13 +0200</pubDate>
       
       <guid>https://relentlesscoding.com/posts/adding-a-file-to-the-root-of-your-gradle-distribution/</guid>
       <description>&lt;p&gt;A while ago, I needed to write a program that retrieved some informationfrom the database by using SQL queries located in files. I wanted to adda file that would contain the settings of the app, like the port, SID,login and password for the database, but also the location of thosequeries. The problem was I couldn&amp;rsquo;t add those settings to a file in thestandard &lt;code&gt;src/groovy/resources&lt;/code&gt; folder, because when creating adistribution with Gradle &lt;code&gt;./gradlew distZip&lt;/code&gt; the file would be part ofthe jar, and thus it would be difficult to modify the contents. I wasbasically after a run-control file, a settings file in the root of mydistribution, that I could easily change on each run (if necessary).&lt;/p&gt;&lt;p&gt;Gradle provides several ways to accomplish this:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Add the file to &lt;code&gt;src/dist&lt;/code&gt; and the file will be put in the root ofthe distribution after invoking &lt;code&gt;./gradlew installDist&lt;/code&gt;. Any foldersyou create will also be put in the distribution, so you can create&lt;code&gt;src/dist/config&lt;/code&gt;, for example, to put your configuration in, and afolder &lt;code&gt;config&lt;/code&gt; will be created in the distribution.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Indicate to Gradle that you want a certain file or folder copied:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;applicationDistribution&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;config.properties&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;into&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Use the&lt;a href=&#34;https://docs.gradle.org/current/userguide/distribution_plugin.html&#34;&gt;&lt;code&gt;distribution&lt;/code&gt;&lt;/a&gt;plugin:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;apply&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;plugin:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;distribution&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;distributions&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;main&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;contents&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;s1&#34;&gt;&amp;#39;settings.groovy&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;</description>
     </item>
   
 </channel>
</rss>
