<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alexander Girke</title>
    <description>The latest articles on DEV Community by Alexander Girke (@alxgrk).</description>
    <link>https://dev.to/alxgrk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F440205%2F01926e2b-8ba7-4c61-a0bf-54fdfd66adcc.jpeg</url>
      <title>DEV Community: Alexander Girke</title>
      <link>https://dev.to/alxgrk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alxgrk"/>
    <language>en</language>
    <item>
      <title>How useful are listings? 📜</title>
      <dc:creator>Alexander Girke</dc:creator>
      <pubDate>Fri, 06 Jan 2023 22:33:24 +0000</pubDate>
      <link>https://dev.to/alxgrk/how-useful-are-listings-1p3p</link>
      <guid>https://dev.to/alxgrk/how-useful-are-listings-1p3p</guid>
      <description>&lt;p&gt;I recently received some DEV credits that can be used to create a listing.&lt;/p&gt;

&lt;p&gt;Since I've never really used / scrolled through the listing section on DEV, I was wondering how useful these credits are. Let's assume I would like to promote a side project, who would I be able to reach with that?&lt;/p&gt;

&lt;p&gt;Does anyone have further information about listings in general and their effectiveness?&lt;/p&gt;

</description>
      <category>emptystring</category>
    </item>
    <item>
      <title>OSS Sponsoring by Usage? 💰📈</title>
      <dc:creator>Alexander Girke</dc:creator>
      <pubDate>Tue, 20 Dec 2022 14:12:33 +0000</pubDate>
      <link>https://dev.to/alxgrk/oss-sponsoring-by-usage-1o8n</link>
      <guid>https://dev.to/alxgrk/oss-sponsoring-by-usage-1o8n</guid>
      <description>&lt;p&gt;Hi there,&lt;/p&gt;

&lt;p&gt;I recently thought about all the different OSS libraries I use every day at work and in personal projects.&lt;/p&gt;

&lt;p&gt;Most of them are created and maintained by individuals that spend a lot of their time on that work. So, to give something back seems like a good thing to do - but to whom?&lt;/p&gt;

&lt;p&gt;What I was asking myself is if there is already a tool/method to track the actual usage of a certain library or all dependencies of a project in general - and then use that data to aggregate &amp;amp; distribute donations to those people using the well-known platforms like &lt;a href="https://github.com/sponsors"&gt;GitHub Sponsors&lt;/a&gt;, &lt;a href="https://www.buymeacoffee.com/"&gt;Buy Me A Coffee&lt;/a&gt;, &lt;a href="https://opencollective.com/"&gt;Open Collective&lt;/a&gt;, and all the others...&lt;/p&gt;

&lt;p&gt;If there is no such thing, do you think people might be interested in something like that?&lt;br&gt;
If so, what could the actual measure for a donation look like? Fixed rate per build / install task? Times the occurrence of that library in the dependency tree? Or a more sophisticated mechanism?&lt;/p&gt;

&lt;p&gt;Please comment below, if you have any other ideas or anything to add 😊&lt;/p&gt;

</description>
    </item>
    <item>
      <title>#RedisHackathon - Comics Auctioning System</title>
      <dc:creator>Alexander Girke</dc:creator>
      <pubDate>Mon, 29 Aug 2022 23:59:39 +0000</pubDate>
      <link>https://dev.to/alxgrk/redishackathon-comics-auctioning-system-1750</link>
      <guid>https://dev.to/alxgrk/redishackathon-comics-auctioning-system-1750</guid>
      <description>&lt;h3&gt;
  
  
  Overview of My Submission
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://redis-auctioning.alxgrk.de/"&gt;https://redis-auctioning.alxgrk.de/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;Microservice Mavens&lt;/p&gt;

&lt;h3&gt;
  
  
  Language Used
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Kotlin/Ktor&lt;/li&gt;
&lt;li&gt;React/TS&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Link to Code
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/alxgrk"&gt;
        alxgrk
      &lt;/a&gt; / &lt;a href="https://github.com/alxgrk/redis-dev-to-hackathon-auction-system"&gt;
        redis-dev-to-hackathon-auction-system
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Contribution to the Redis-Dev.to-Hackathon.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Meme Auctioning System&lt;/h1&gt;
&lt;p&gt;Finally, you are able to sell your comics, memes and other artistic drawings on an auctioning platform.&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/alxgrk/redis-dev-to-hackathon-auction-system./docs/1.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kdGec1gs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/alxgrk/redis-dev-to-hackathon-auction-system./docs/1.png" alt="Dashboard" width="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/alxgrk/redis-dev-to-hackathon-auction-system./docs/2.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OqH6etXa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/alxgrk/redis-dev-to-hackathon-auction-system./docs/2.png" alt="Item Search" width="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/alxgrk/redis-dev-to-hackathon-auction-system./docs/3.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PZi8r0u7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/alxgrk/redis-dev-to-hackathon-auction-system./docs/3.png" alt="Ended Auction" width="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/alxgrk/redis-dev-to-hackathon-auction-system./docs/4.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jMYKMi3w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/alxgrk/redis-dev-to-hackathon-auction-system./docs/4.png" alt="Open Auction" width="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
How it works&lt;/h2&gt;

&lt;h3&gt;
How the data is stored:&lt;/h3&gt;
&lt;p&gt;Most of the data is stored as JSON in various keys and various data types.&lt;/p&gt;
&lt;div class="highlight highlight-source-yaml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;  &lt;span class="pl-ent"&gt;Refresh Tokens&lt;/span&gt;
     &lt;span class="pl-ent"&gt;userId&lt;/span&gt;: &lt;span class="pl-s"&gt;like "cd63d6fd-3333-4877-9b8f-aab9e24271ca"&lt;/span&gt;
     &lt;span class="pl-ent"&gt;refreshToken&lt;/span&gt;: &lt;span class="pl-s"&gt;like "c3adfc0d-6eb6-40f7-8663-49896d0c1855"&lt;/span&gt;
     &lt;span class="pl-ent"&gt;expiryDate&lt;/span&gt;: &lt;span class="pl-s"&gt;like 1662422171471&lt;/span&gt;

  &lt;span class="pl-ent"&gt;Auctions&lt;/span&gt;:
     &lt;span class="pl-ent"&gt;id&lt;/span&gt;: &lt;span class="pl-s"&gt;like "8b0be9f2-3bb0-4972-87c6-423a7073832f"&lt;/span&gt;
     &lt;span class="pl-ent"&gt;start&lt;/span&gt;: &lt;span class="pl-s"&gt;like "2022-08-07T22:23:07Z"&lt;/span&gt;
     &lt;span class="pl-ent"&gt;end&lt;/span&gt;: &lt;span class="pl-s"&gt;like "2022-08-10T22:23:07Z"&lt;/span&gt;
     &lt;span class="pl-ent"&gt;items&lt;/span&gt;: &lt;span class="pl-s"&gt;like ["95069680-670a-40c6-8068-c6cdd2561932"]&lt;/span&gt;
     &lt;span class="pl-ent"&gt;title&lt;/span&gt;: &lt;span class="pl-s"&gt;like "First Auction"&lt;/span&gt;
     &lt;span class="pl-ent"&gt;description&lt;/span&gt;: &lt;span class="pl-s"&gt;like "The very first Auction"&lt;/span&gt;
     &lt;span class="pl-ent"&gt;seller&lt;/span&gt;: &lt;span class="pl-s"&gt;like "cd63d6fd-3333-4877-9b8f-aab9e24271ca"&lt;/span&gt;
     &lt;span class="pl-ent"&gt;lowestBid&lt;/span&gt;:
       &lt;span class="pl-ent"&gt;amount&lt;/span&gt;: &lt;span class="pl-s"&gt;like 123.45&lt;/span&gt;
       &lt;span class="pl-ent"&gt;currency&lt;/span&gt;: &lt;span class="pl-s"&gt;like "EUR'&lt;/span&gt;
     &lt;span class="pl-ent"&gt;isClosed&lt;/span&gt;: &lt;span class="pl-s"&gt;like false&lt;/span&gt;

  &lt;span class="pl-ent"&gt;Items&lt;/span&gt;:
     &lt;span class="pl-ent"&gt;id&lt;/span&gt;: &lt;span class="pl-s"&gt;like "d6118270-8404-4560-bee9-50c32870dd69"&lt;/span&gt;
     &lt;span class="pl-ent"&gt;title&lt;/span&gt;: &lt;span class="pl-s"&gt;like "Move Fast and Break Things"&lt;/span&gt;
     &lt;span class="pl-ent"&gt;description&lt;/span&gt;: &lt;span class="pl-s"&gt;like "Move Fast and Break Things"&lt;/span&gt;
     &lt;span class="pl-ent"&gt;image&lt;/span&gt;: &lt;span class="pl-s"&gt;like "https://imgs.xkcd.com/comics/move_fast_and_break_things_2x.png"&lt;/span&gt;
     &lt;span class="pl-ent"&gt;owner&lt;/span&gt;: &lt;span class="pl-s"&gt;like "735db4d6-658d-4ea6-aeed-d0718e076d6a"&lt;/span&gt;
  
  &lt;span class="pl-ent"&gt;Biddings&lt;/span&gt;: &lt;span class="pl-s"&gt;(where key has prefix 'biddings:' followed by auctionId)&lt;/span&gt;
    &lt;span class="pl-ent"&gt;0&lt;/span&gt;:
     &lt;span class="pl-ent"&gt;id&lt;/span&gt;: &lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/alxgrk/redis-dev-to-hackathon-auction-system"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
`

&lt;p&gt;BTW, your Github code’s README file should contain the details as &lt;a href="https://github.com/redis-developer/hackathon-docs/blob/main/README.md"&gt;per this template&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;[Note:] # (Be sure to link to any open source projects that are using your workflow!)&lt;/p&gt;

&lt;p&gt;[Note:] # Screenshots/demo videos are encouraged!&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Check out &lt;a href="https://redis.io/docs/stack/get-started/clients/#high-level-client-libraries"&gt;Redis OM&lt;/a&gt;, client libraries for working with Redis as a multi-model database.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Use &lt;a href="https://redis.info/redisinsight"&gt;RedisInsight&lt;/a&gt; to visualize your data in Redis.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Sign up for a &lt;a href="https://redis.info/try-free-dev-to"&gt;free Redis database&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>redishackathon</category>
    </item>
    <item>
      <title>Tracking Twitter User Behavior - The Browser Extension</title>
      <dc:creator>Alexander Girke</dc:creator>
      <pubDate>Mon, 01 Feb 2021 08:20:20 +0000</pubDate>
      <link>https://dev.to/alxgrk/tracking-twitter-user-behavior-the-browser-extension-2aeh</link>
      <guid>https://dev.to/alxgrk/tracking-twitter-user-behavior-the-browser-extension-2aeh</guid>
      <description>&lt;p&gt;Hello👋,&lt;/p&gt;

&lt;p&gt;today we're gonna have a look at the first component of my data collection pipeline: the &lt;strong&gt;browser extension&lt;/strong&gt;. If you want to peek into the code, find it &lt;a href="https://github.com/alxgrk/twitter-tracking/tree/master/twitter-tracking-browser-extension"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Intro
&lt;/h1&gt;

&lt;p&gt;First, some background: what I needed was a browser extension, which runs on as many browsers as possible without having too much of a hassle supporting all of them. Luckily, &lt;a href="https://developer.chrome.com/docs/extensions/reference/"&gt;Chrome&lt;/a&gt; (and all other Chromium-based browsers like &lt;a href="https://www.opera.com/de"&gt;Opera&lt;/a&gt;, &lt;a href="https://www.microsoft.com/en-us/edge"&gt;Edge&lt;/a&gt; or &lt;a href="https://brave.com/"&gt;Brave&lt;/a&gt;) and &lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API"&gt;Firefox&lt;/a&gt; basically share the same API, called &lt;strong&gt;&lt;em&gt;WebExtensions API&lt;/em&gt;&lt;/strong&gt;. I won't go into much detail, so if you're not familiar with how browser extensions look like and what they can do, check out this awesome post:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/ganeshjaiwal" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---sTroaJc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--QRw-r2Xb--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/462671/67f6eaf1-e1af-48dd-b13f-77a20e4412dd.png" alt="ganeshjaiwal"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/ganeshjaiwal/build-your-first-chrome-extension-458g" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Build Your First Chrome Extension&lt;/h2&gt;
      &lt;h3&gt;Ganesh Jaiwal ・ Sep 3 '20 ・ 4 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#codenewbie&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#computerscience&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;The resulting manifest.json looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Twitter Tracking Extension"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$VERSION"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A browser extension that captures Twitter clicks - for scientific reasons."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"homepage_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/alxgrk/twitter-tracking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browser_specific_settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"gecko"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"twittertracking@university.de"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"strict_min_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"48.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"update_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/alxgrk/twitter-tracking/blob/master/twitter-tracking-browser-extension/firefox-addon-update.json"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"storage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"unlimitedStorage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"$ACCESS_SITE_PERMISSION"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"48"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/icon-48.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"96"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/icon-96.png"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"browser_action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"default_icon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"32"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/icon-32.png"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"default_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Twitter Tracking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"default_popup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"popup/tracked-events.html"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"content_scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*://*.twitter.com/*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"js"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"js/main.js"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the variables starting with &lt;code&gt;$...&lt;/code&gt;: they are going to be replaced by webpack. We'll see how in the next section.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Building Process
&lt;/h1&gt;

&lt;p&gt;First, let's have a look at how the final artifacts are build. I'm using Webpack's &lt;code&gt;copy-webpack-plugin&lt;/code&gt; and &lt;code&gt;webpack.DefinePlugin&lt;/code&gt; to differentiate when building for dev and prod. The CopyWebpackPlugin enables you to move third-party dependencies where needed or replace placeholders in files like the &lt;code&gt;manifest.json&lt;/code&gt;. The following snippet shows how to set version and access site permission &amp;amp; bundle the &lt;code&gt;webextension-polyfills&lt;/code&gt; necessary to ensure interoperability between Chrome and Firefox for production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;CopyWebpackPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;globOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;ignore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/*.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="nx"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;absoluteFrom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;absoluteFrom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;manifest.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$VERSION&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;npm_package_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$ACCESS_SITE_PERMISSION&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROD_API_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node_modules/webextension-polyfill/dist/browser-polyfill.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To set environment variables like the endpoint of the REST-API, DefinePlugin has to be used as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DefinePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;process.env.NODE_ENV&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"production"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;API&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROD_API_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you may have noticed, the value for the &lt;code&gt;API&lt;/code&gt; comes from a variable named &lt;code&gt;dotenv&lt;/code&gt;, which is also the name of the library, that reads a &lt;code&gt;.env&lt;/code&gt; file from your current working directory and provides its values as JS objects. To optimize the production artifact, I also added the TerserPlugin like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;optimization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;minimize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;minimizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TerserPlugin&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we now have everything ready to build our artifacts, we need to make sure that Chrome, Firefox, etc. permit their installation. To achieve this, we need two different tools: &lt;code&gt;web-ext&lt;/code&gt; for Firefox and &lt;code&gt;chromium&lt;/code&gt; for Chrome. Check out &lt;a href="https://github.com/alxgrk/twitter-tracking/blob/master/twitter-tracking-browser-extension/bundle-for-firefox-and-chrome.sh"&gt;this script&lt;/a&gt; for an example on how to use them. Also, make sure to follow the instructions on publishing extensions for &lt;a href="https://extensionworkshop.com/documentation/publish/signing-and-distribution-overview/"&gt;Firefox&lt;/a&gt; and for &lt;a href="https://developer.chrome.com/docs/webstore/publish/"&gt;Chrome&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Collection Process
&lt;/h1&gt;

&lt;p&gt;Once the user has the extension installed, the content-script located at &lt;code&gt;src/main.js&lt;/code&gt; runs as soon as the user visits Twitter. And now the funny stuff begins.&lt;/p&gt;

&lt;p&gt;What I did was to register some listeners to the standard browser events like &lt;code&gt;load&lt;/code&gt;, &lt;code&gt;beforeunload&lt;/code&gt;,&lt;code&gt;click&lt;/code&gt; or &lt;code&gt;scroll&lt;/code&gt;. If you remember the model from &lt;a href="https://dev.to/alxgrk/tracking-twitter-user-behavior-the-model-44cf"&gt;the previous post&lt;/a&gt;, we now need to deduct from these events what temporal dimension and what kind of user interactions we have. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;load&lt;/code&gt; and &lt;code&gt;beforeunload&lt;/code&gt; give us the &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; events, which are going to be published to the backend and additionally stored in local storage for the user to be displayed later. While constructing the events, we also have to create some kind of identifier for the user, ideally a reproducible one if we want to relate his/her Android app interactions. To achieve this, the username has to be found by inspecting the &lt;code&gt;alt&lt;/code&gt; attribute of the user's profile image. And since we care (at least a little) about privacy, a consistent and &lt;em&gt;"secure"&lt;/em&gt; hashing method like &lt;em&gt;SHA256&lt;/em&gt; is applied to it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;scroll&lt;/code&gt; maps trivially to the scroll events needed. Just use &lt;code&gt;document.documentElement.scrollTop&lt;/code&gt; to get the current scroll position.&lt;/p&gt;

&lt;p&gt;Concerning &lt;code&gt;click&lt;/code&gt;s, however, a little more fiddling is required. Unfortunately, Twitter HTML tags are almost all &lt;code&gt;div&lt;/code&gt;s with randomized classnames. The only anchor are semantic tags like &lt;code&gt;article&lt;/code&gt;, rare element IDs and the attribute &lt;code&gt;data-testid&lt;/code&gt;. Luckily, with these and a great tool named &lt;a href="https://github.com/antonmedv/finder"&gt;&lt;code&gt;@medv/finder&lt;/code&gt;&lt;/a&gt;, I was able to create RegExes that match the found selector to my predefined click types. So for any click events, the &lt;code&gt;finder&lt;/code&gt; is run...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;finder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="c1"&gt;// do not rely on classnames&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and the result is mapped. All mapping functions are stored in an object, whose keys are the names of the click type. For e.g. a like click, this is the RegEx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TARGETS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;like&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sr"&gt;/#tweet-action-buttons.+:nth-child&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="sr"&gt;3&lt;/span&gt;&lt;span class="se"&gt;\)&lt;/span&gt;&lt;span class="sr"&gt;.*/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can imagine, this approach is quite error-prone. But that is one of the difficulties when reverse-engineering a constantly changing external system and I haven't found an alternative yet. If you know it better, please comment.&lt;/p&gt;

&lt;p&gt;Thanks for reading and have fun until next time when the post about the &lt;strong&gt;Android app&lt;/strong&gt; is finished. ✌️&lt;/p&gt;

</description>
      <category>twitter</category>
      <category>browser</category>
      <category>extension</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Tracking Twitter User Behavior - The Model</title>
      <dc:creator>Alexander Girke</dc:creator>
      <pubDate>Mon, 25 Jan 2021 07:46:26 +0000</pubDate>
      <link>https://dev.to/alxgrk/tracking-twitter-user-behavior-the-model-44cf</link>
      <guid>https://dev.to/alxgrk/tracking-twitter-user-behavior-the-model-44cf</guid>
      <description>&lt;p&gt;Hello again👋,&lt;br&gt;
welcome to the second part of my series on understanding Twitter user behavior.&lt;/p&gt;

&lt;p&gt;Before we dive deeper into the code, I want you to understand how I envision the characteristics of an user's behavior.&lt;/p&gt;

&lt;p&gt;First of all, there are three main aspects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;temporal&lt;/strong&gt; 🕰️ - how often does the user open Twitter and how long does a session last?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;publicly visible&lt;/strong&gt; 📻 - how often does a user post a tweet or follow someone?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;publicly invisible&lt;/strong&gt; 🕵️‍♀️ - how many tweets does a user scroll, how often do they click on images, video, etc. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For each of those three aspects I assigned some quantifiable characteristics. The result looks like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Temporal&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Publicly Visible&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Publicly Invisible&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sessions Per Day&lt;/td&gt;
&lt;td&gt;Posting Prob.&lt;/td&gt;
&lt;td&gt;Scroll Timeline Prob.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session Length&lt;/td&gt;
&lt;td&gt;Like Prob.&lt;/td&gt;
&lt;td&gt;Click On Media Prob.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session Distribution Over Day&lt;/td&gt;
&lt;td&gt;Retweet Prob.&lt;/td&gt;
&lt;td&gt;Open Details Prob.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Follow After Searching Prob.&lt;/td&gt;
&lt;td&gt;Click On Author Profile Prob.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Follow From Tweet Prob.&lt;/td&gt;
&lt;td&gt;Click On Hashtag Prob.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So the next step is to collect data from real users that will allow me to assign values to each of these cells. To do this, the Android app and the browser extension will have to send events that correspond to what can be described by this Kotlin data class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;EventType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;scrollPosition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;estimatedTweetsScrolled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;BROWSER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;ANDROID&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, some of the properties are optional, depending on the value for &lt;code&gt;action&lt;/code&gt;, which is either &lt;code&gt;scroll&lt;/code&gt;, &lt;code&gt;click&lt;/code&gt;, &lt;code&gt;session_start&lt;/code&gt; or &lt;code&gt;session_end&lt;/code&gt;. &lt;em&gt;Note that this is the data class used in the backend, which is responsible for writing into the ElasticSearch instance only - hence, I didn't need to refine it anymore. The Android app, however, uses a more complex class hierarchy to facilitate event publishing. More on this in a later post.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A like event in the browser would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BROWSER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"b92427ae41e4649b934ca495991b7852b855..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-01-24T16:11:36.822Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"selector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#tweet-action-buttons &amp;gt; div:nth-child(3) &amp;gt; div &amp;gt; div &amp;gt; div &amp;gt; div"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... whereas a scroll event on Android would look similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ANDROID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3a4cab1a6f566207d520622e0bbce78d7..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scroll"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-01-24T13:29:33.853"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scroll_position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5938&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"estimated_tweets_scrolled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this information it is later possible to identify sessions, probabilities per session and probabilities per tweet. Thus, in the end, &lt;a href="https://dev.to/alxgrk/spring-library-selenium-docker-pool-4ja0"&gt;automated software&lt;/a&gt; that tries to mimic real users can be fed with these values.&lt;/p&gt;

&lt;p&gt;Those are the basics to get an idea of what the whole infrastructure is about. In the next posts, we will look at the individual components, starting with the browser extension.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For those who haven't read the first post: &lt;a href="https://github.com/alxgrk/twitter-tracking"&gt;here&lt;/a&gt; is the code involved.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>twitter</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Tracking Twitter User Behavior - The Overview</title>
      <dc:creator>Alexander Girke</dc:creator>
      <pubDate>Sat, 23 Jan 2021 21:49:30 +0000</pubDate>
      <link>https://dev.to/alxgrk/tracking-twitter-user-behavior-the-overview-140m</link>
      <guid>https://dev.to/alxgrk/tracking-twitter-user-behavior-the-overview-140m</guid>
      <description>&lt;p&gt;Hey there👋,&lt;/p&gt;

&lt;p&gt;it's been a while since my last post - which doesn't mean, I've been lazy. I've constantly worked on my Master's Thesis and I'm almost done, but now I really wanted to share what I recently did: understanding Twitter users' behavior by spying on them.&lt;/p&gt;

&lt;p&gt;In fact, one shouldn't call it spying if the victim wants it that way - or should one (&lt;a href="https://www.irishtimes.com/news/consumer/targeted-ads-is-social-media-spying-on-us-1.3375310?mode=amp"&gt;#socialmedia&lt;/a&gt;)? Nonetheless, some people were crazy enough to help me by installing a browser extension or an Android app, respectively. And in this series I want to show you the infrastructure I used to collect and analyze the data they produced.&lt;/p&gt;

&lt;p&gt;So let's start with an overview:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KXLEaRCU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yij9qu0soucist8ru9wg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KXLEaRCU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yij9qu0soucist8ru9wg.png" alt="Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you might see, I used &lt;a href="https://aws.com/"&gt;AWS&lt;/a&gt; to deploy some of the components: I've created a Lambda function, which serves a REST API through an API Gateway and is based on &lt;a href="https://ktor.io/"&gt;Ktor&lt;/a&gt; Kotlin server framework. On invocation, incoming events are stored in an &lt;a href="https://elastic.co/elastic-stack"&gt;ElasticSearch&lt;/a&gt; instance also provided by AWS. Best of all: all of this is covered by the AWS Free Tier, so there is no cost.&lt;/p&gt;

&lt;p&gt;To analyse the data, I again used Kotlin and combined 3 incredibly helpful tools: &lt;a href="https://github.com/ajalt/clikt"&gt;clikt&lt;/a&gt;, &lt;a href="https://github.com/jillesvangurp/es-kotlin-client"&gt;es-kotlin-client&lt;/a&gt; and &lt;a href="https://github.com/mipt-npm/plotly.kt"&gt;plotly.kt&lt;/a&gt;. But more about that later.&lt;/p&gt;

&lt;p&gt;You might know that tracking clicks in a browser is no big deal - but how the heck is that supposed to work in an Android app? Sorry, but again you'll have to be patient 🤫 - that's part of the next article in this series.&lt;/p&gt;

&lt;p&gt;For those who don't want to wait for the solution: check out the source code of the whole infrastructure &lt;a href="https://github.com/alxgrk/twitter-tracking"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Hope you'll stay tuned. ✌️&lt;/p&gt;

</description>
      <category>twitter</category>
      <category>aws</category>
      <category>android</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>TIL: Connecting to the host from Testcontainers</title>
      <dc:creator>Alexander Girke</dc:creator>
      <pubDate>Mon, 02 Nov 2020 22:51:23 +0000</pubDate>
      <link>https://dev.to/alxgrk/til-connecting-to-the-host-from-testcontainers-55if</link>
      <guid>https://dev.to/alxgrk/til-connecting-to-the-host-from-testcontainers-55if</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TL;DR: &lt;a href="https://www.testcontainers.org/"&gt;Testcontainers&lt;/a&gt; allows you to expose a host's port to containers using &lt;code&gt;Testcontainers.exposeHostPorts(localServerPort);&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A short reminder: for my Master's thesis I have to work with Selenium docker containers to automate Twitter accounts. My latest task was to make it possible to specify a proxy, which should be used by the Chrome browser running inside a container. That way, it want to fool Twitter about the true origin of an account, which is especially interesting if you want to automate multiple accounts at the same time on the same machine.&lt;/p&gt;

&lt;p&gt;While setting the proxy has to be done using Selenium's &lt;code&gt;JavascriptExecutor.executeScript(..)&lt;/code&gt; method in conjunction with JavaScript code accessing the &lt;a href="https://developer.chrome.com/extensions/proxy"&gt;Proxy API for Extensions&lt;/a&gt; &lt;em&gt;(for more information, have a look at the example in &lt;a href="https://github.com/alxgrk/spring-selenium-pool/#-custom-chrome-extension"&gt;spring-selenium-pool&lt;/a&gt;)&lt;/em&gt;, I finally wanted to test my setup.&lt;/p&gt;

&lt;p&gt;As a mock proxy server, I decided to use &lt;a href="https://mock-server.com/"&gt;MockServer&lt;/a&gt; - integration with Java/Kotlin is easy, as is defining stub responses. The final question however was: how could I connect to the MockServer running on the host from inside the Selenium container?&lt;/p&gt;

&lt;p&gt;Luckily, Testcontainers guys got me covered. The &lt;a href="https://www.testcontainers.org/features/networking/#exposing-host-ports-to-the-container"&gt;documentation&lt;/a&gt; provided the answer: &lt;code&gt;Testcontainers.exposeHostPorts(localServerPort);&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's all we need. Just make sure to run that line &lt;strong&gt;before&lt;/strong&gt; your container starts, but &lt;strong&gt;after&lt;/strong&gt; the mock server is running.&lt;/p&gt;

&lt;p&gt;The host will then be available from inside the container at &lt;code&gt;host.testcontainers.internal&lt;/code&gt; + your random local port.&lt;/p&gt;

&lt;p&gt;What I understood it does under the hood, is to start another Docker container with an ssh-server that uses &lt;a href="https://en.wikipedia.org/wiki/Port_forwarding"&gt;port forwarding&lt;/a&gt; to route all TCP traffic from the container of interest through the ssh-container to the host.&lt;/p&gt;

&lt;p&gt;So in the end, here is the skeleton of the resulting test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;TaskWithProxyTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;webEnvironment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SpringBootTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WebEnvironment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NONE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;properties&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"selenium.pool.size=1"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaskWithProxyTest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// causes the container from spring-selenium-pool to come up&lt;/span&gt;
    &lt;span class="nd"&gt;@SpringBootApplication&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;

    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;mockProxy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ClientAndServer&lt;/span&gt;

        &lt;span class="nd"&gt;@BeforeAll&lt;/span&gt;
        &lt;span class="nd"&gt;@JvmStatic&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;mockServerPort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SocketUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAvailableTcpPort&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;mockProxy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;startClientAndServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockServerPort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;// necessary to access mockserver from container&lt;/span&gt;
            &lt;span class="nc"&gt;Testcontainers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exposeHostPorts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockServerPort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;@AfterAll&lt;/span&gt;
        &lt;span class="nd"&gt;@JvmStatic&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;tearDown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;mockProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;`your&lt;/span&gt; &lt;span class="nf"&gt;test`&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>testing</category>
      <category>todayilearned</category>
      <category>java</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Playing around with Kotlin Sealed Classes</title>
      <dc:creator>Alexander Girke</dc:creator>
      <pubDate>Thu, 24 Sep 2020 06:50:15 +0000</pubDate>
      <link>https://dev.to/alxgrk/playing-around-with-kotlin-sealed-classes-4kdh</link>
      <guid>https://dev.to/alxgrk/playing-around-with-kotlin-sealed-classes-4kdh</guid>
      <description>&lt;p&gt;While working on my Master's thesis, I recently had to configure probabilities. These values were organized in a tree-like structure. Because of this, I thought of using inheritance - and with Kotlin already in place, using sealed classes seemed at least like an interesting opportunity to me.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; This post is just for fun and for showing some cool Kotlin-specific things. In the end, using Jackson to deserialize input into data classes is probably more effective.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Sample
&lt;/h2&gt;

&lt;p&gt;Let's say, we want to configure the likeliness in percent of some actions using YAML for instance. This could look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Configuration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Greeting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;SayHello&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;70&lt;/span&gt;
        &lt;span class="na"&gt;WaveAt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
        &lt;span class="na"&gt;Hug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="na"&gt;Talking&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;DoSmallTalk&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;
        &lt;span class="na"&gt;Insult&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, there are two subsections. However, this can easily get an arbitrary depth.&lt;/p&gt;

&lt;p&gt;Before we'll reproduce that structure, we should clarify what sealed classes are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sealed Classes
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://kotlinlang.org/docs/reference/sealed-classes.html"&gt;official documentation&lt;/a&gt; calls them extensions to enum classes, used for representing restricted class hierarchies.&lt;/p&gt;

&lt;p&gt;Sealed classes are declared using the keyword &lt;code&gt;sealed&lt;/code&gt;, are abstract and must have only private constructors.&lt;/p&gt;

&lt;p&gt;Simply put, a sealed class ensures, that &lt;strong&gt;all&lt;/strong&gt; possible subtypes are known at compile time. This comes especially handy, when used in conjunction with Kotlin's &lt;code&gt;when&lt;/code&gt; clause:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TruthOrDare&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Truth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TruthOrDare&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TruthOrDare&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;nextTurn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TruthOrDare&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;Truth&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Answer the following question: ${input.question}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;Dare&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Your task is: ${input.task}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// the `else` clause is not required because we've covered all the cases&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sealed Classes in Action
&lt;/h2&gt;

&lt;p&gt;Let's come back to the original task: using sealed classes for parsing configuration.&lt;/p&gt;

&lt;p&gt;The structure of the above YAML snippet can be represented as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;SayHello&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;WaveAt&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Hug&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Talking&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;DoSmallTalk&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Talking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Insult&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Talking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have nested sealed subclasses and &lt;code&gt;object&lt;/code&gt;s (a shortcut for &lt;a href="https://en.wikipedia.org/wiki/Singleton_pattern"&gt;Singletons&lt;/a&gt; in Kotlin). We'll see later, why we are using objects here.&lt;/p&gt;

&lt;p&gt;However, yet there is nothing configured. So how do we use this structure?&lt;/p&gt;

&lt;p&gt;The answer is: &lt;a href="https://kotlinlang.org/docs/reference/reflection.html"&gt;Reflection&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Digression
&lt;/h3&gt;

&lt;p&gt;Before we come to the actual structure though, I would like to introduce a small wrapper class, to encapsulate our findings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Probabilities&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;backingMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MutableMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mutableMapOf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;backingMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Probabilities&lt;/span&gt;&lt;span class="p"&gt;.()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;infix&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withProbabilityOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;percent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;backingMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;percent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;backingMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;joinToString&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"${it.key::class.simpleName} = ${it.value}"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this class you can see some other cool Kotlin features: &lt;a href="https://kotlinlang.org/docs/reference/delegation.html"&gt;&lt;strong&gt;implementation by delegation&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://kotlinlang.org/docs/reference/lambdas.html#function-types"&gt;&lt;strong&gt;receiver functions&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://kotlinlang.org/docs/reference/functions.html#infix-notation"&gt;&lt;strong&gt;infix functions&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Implementing the &lt;code&gt;Map&lt;/code&gt; interface can be a pain in Java. Kotlin provides a smart solution with &lt;strong&gt;implementation by delegation&lt;/strong&gt;. All you need to do, is to use the &lt;code&gt;by&lt;/code&gt; keyword in conjunction with a value, that already implements the interface. So in this case we simply delegate all &lt;code&gt;Map&lt;/code&gt;-specific operations to a &lt;code&gt;MutableMap&lt;/code&gt;, which is created by the default constructor.&lt;/p&gt;

&lt;p&gt;To make configuration of the &lt;code&gt;Probabilities&lt;/code&gt; class easy, we provide a second constructor taking a &lt;strong&gt;receiver function&lt;/strong&gt;. This means, that the &lt;code&gt;this&lt;/code&gt; keyword of the provided lambda points to an instance of &lt;code&gt;Probabilities&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;Another &lt;strong&gt;receiver function&lt;/strong&gt; is also provided by &lt;code&gt;withProbabilityOf&lt;/code&gt;. However, this is also an &lt;strong&gt;infix function&lt;/strong&gt;, which is marked by the &lt;code&gt;infix&lt;/code&gt; keyword and enables you to write something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nc"&gt;SayHello&lt;/span&gt; &lt;span class="n"&gt;withProbabilityOf&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Parsing
&lt;/h2&gt;

&lt;p&gt;Finally, here is the code to parse e.g. a &lt;code&gt;Map&lt;/code&gt; of &lt;code&gt;String&lt;/code&gt; and &lt;code&gt;Any&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;fromMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Probabilities&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// (1)&lt;/span&gt;
    &lt;span class="nd"&gt;@Suppress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UNCHECKED_CAST"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;parseFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="c1"&gt;// (2)&lt;/span&gt;
            &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="c1"&gt;// (3)&lt;/span&gt;
            &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;KClass&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;?,&lt;/span&gt;
            &lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;KClass&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// (4)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;containsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;simpleName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;isSuperclassOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;simpleName&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// (5)&lt;/span&gt;
                    &lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sealedSubclasses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;subclass&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="nf"&gt;parseFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subclass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// (6)&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objectInstance&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${clazz.simpleName} should be an object"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                    &lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objectInstance&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt; &lt;span class="n"&gt;withProbabilityOf&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unknown property ${clazz.simpleName}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;parseFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some explanation (mind the comments in code):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;(1)&lt;/strong&gt; we construct a new instance of &lt;code&gt;Probabilities&lt;/code&gt; by making use of one of Kotlin's greatest features: if the last parameter of any function or constructor is a lambda function, we can omit parentheses and only use curly braces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(2)&lt;/strong&gt; another beautiful thing are &lt;code&gt;local functions&lt;/code&gt;: since we don't need the recursive function &lt;code&gt;parseFor&lt;/code&gt; anywhere else, we simply declare it inside our &lt;code&gt;fromMap&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(3)&lt;/strong&gt; the parameters of our &lt;code&gt;parseFor&lt;/code&gt; function are:

&lt;ul&gt;
&lt;li&gt;the subtree of the configuration&lt;/li&gt;
&lt;li&gt;the assumed parent as specified by the configuration&lt;/li&gt;
&lt;li&gt;the current class as specified by the configuration's property&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(4)&lt;/strong&gt; if somewhere on top level of the current subtree the expected class is found and the assumed parent matches the real superclass, then we use &lt;code&gt;when&lt;/code&gt; on the property value's type&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(5)&lt;/strong&gt; if it's a &lt;code&gt;Map&lt;/code&gt;, apply &lt;code&gt;parseFor&lt;/code&gt; on each sealed subclass&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(6)&lt;/strong&gt; if it's an &lt;code&gt;Int&lt;/code&gt; and &lt;code&gt;clazz&lt;/code&gt; is an &lt;code&gt;object&lt;/code&gt;, we configure the probability to be that value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the above code, you might see, why having leaves being &lt;code&gt;object&lt;/code&gt;s is a good idea: there will always be exactly one instance. So they are perfect for being used as a key in a map.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sweet. - But why?
&lt;/h2&gt;

&lt;p&gt;To be honest, all of the above might seem like an over-engineered construct. Compared to the example using sealed classes, an equivalent structure with data classes would be similar and could be easily used with e.g. Jackson out of the box:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;talking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Talking&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;sayHello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;waveAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;hug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Talking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;doSmallTalk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;insult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the great thing about Kotlin: you have so many language constructs that help you build your software the &lt;em&gt;best&lt;/em&gt; way, whatever this means. Therefore, it's necessary to play around with and get to know these possibilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Jackson
&lt;/h2&gt;

&lt;p&gt;To make use of the concept of using sealed classes in conjunction with Jackson, we need to provide a custom deserializer. Since we already have the mapping logic however, this is an easy task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProbabilitiesDeserializer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StdDeserializer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Probabilities&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nc"&gt;Probabilities&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JsonParser&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="n"&gt;ctxt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;DeserializationContext&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;readValueAs&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="err"&gt;: &lt;/span&gt;&lt;span class="nc"&gt;TypeReference&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;()&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
                    &lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;fromMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this and the ability to parse YAML we need the following dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;com.fasterxml.jackson.module:jackson-module-kotlin:2.11.2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11.2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, we are ready to create an &lt;code&gt;ObjectMapper&lt;/code&gt; and read our YAML configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;mapper&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;YAMLFactory&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;registerModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SimpleModule&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;addDeserializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Probabilities&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProbabilitiesDeserializer&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="nf"&gt;registerModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;KotlinModule&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;probabilities&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readValue&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Probabilities&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;yamlInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;probabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Say hello with probability of ${probabilities[SayHello]} percent."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SayHello = 70, WaveAt = 80, Hug = 5, DoSmallTalk = 90, Insult = 1
Say hello with probability of 70 percent.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Ensuring all properties are set
&lt;/h2&gt;

&lt;p&gt;One last thing I wanted to add, is how you enforce all &lt;code&gt;object&lt;/code&gt;s to be configured.&lt;/p&gt;

&lt;p&gt;All we have to do is to find all the leaves of our configuration tree and compare them to the input's content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;leaves&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;leavesOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;leavesOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;KClass&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;):&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;KClass&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;baseClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isSealed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseClass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;baseClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sealedSubclasses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;leavesOf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nc"&gt;Probabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ensureAllActionsCovered&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;keys&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;unconfigured&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;leaves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;leaf&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leaf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unconfigured&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNotEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unconfigured leaves: ${unconfigured.joinToString()}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we've seen a last cool Kotlin feature: &lt;a href="https://kotlinlang.org/docs/reference/extensions.html#extension-functions"&gt;&lt;strong&gt;extension functions&lt;/strong&gt;&lt;/a&gt;. They allow us to add some functionality to an otherwise closed class. Like a bonus in a way.&lt;/p&gt;

&lt;p&gt;You call them as if they were a class method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;probabilities.ensureAllActionsCovered()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Closing notes
&lt;/h2&gt;

&lt;p&gt;Thanks for reading, I hope you liked it. You can find all the code in a &lt;a href="https://github.com/holgerbrandl/kscript"&gt;kscript&lt;/a&gt; on &lt;a href="https://gist.github.com/alxgrk/e672b031206481bc462d21788a321804"&gt;Github as a Gist&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>jackson</category>
      <category>java</category>
    </item>
    <item>
      <title>Kotlin: 'null' does not equal 'true', but also not 'false'</title>
      <dc:creator>Alexander Girke</dc:creator>
      <pubDate>Mon, 21 Sep 2020 14:17:54 +0000</pubDate>
      <link>https://dev.to/alxgrk/kotlin-null-does-not-equal-true-but-also-not-false-3paa</link>
      <guid>https://dev.to/alxgrk/kotlin-null-does-not-equal-true-but-also-not-false-3paa</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Kotlin has the &lt;code&gt;===&lt;/code&gt; operator for referential integrity and none of &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt; or &lt;code&gt;null&lt;/code&gt; references the same thing. Don't forget that, when comparing nullable booleans.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Today I stumbled upon a hint from IntelliJ. I wanted to write something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;potentiallyNull&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="c1"&gt;// in real world, we wouldn't know whether it's null or not&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;potentiallyNull&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"not printed when null"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In words: &lt;em&gt;if &lt;code&gt;potentiallyNull&lt;/code&gt; is not null, check for equality with &lt;code&gt;"foo"&lt;/code&gt;; otherwise return &lt;code&gt;false&lt;/code&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So, IntelliJ kindly told me: &lt;code&gt;Equality check should be used instead of elvis for nullable boolean check&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I thought to myself, alright then, let's apply that nice hint.&lt;/p&gt;

&lt;p&gt;What I got was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;potentiallyNull&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"not printed when null"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, later on, I wanted to invert the condition. So naively, I simply replaced &lt;code&gt;true&lt;/code&gt; by &lt;code&gt;false&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;potentiallyNull&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"print also when null"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, the result was not as expected. There was not a single character printed to the console. &lt;em&gt;What went wrong?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Actually, it's quite obvious, but it took me a moment to realize: &lt;strong&gt;of course, &lt;code&gt;null&lt;/code&gt; is neither the same as &lt;code&gt;true&lt;/code&gt; nor as &lt;code&gt;false&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So, I took a step back and rewrote my original condition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;potentiallyNull&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"print also when null"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IntelliJ again told me to change that and use the equality check, so what I got now was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;potentiallyNull&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"print also when null"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing I thought was: &lt;em&gt;How is &lt;code&gt;... != false&lt;/code&gt; different to &lt;code&gt;... == true&lt;/code&gt;&lt;/em&gt;? &lt;/p&gt;

&lt;p&gt;But again, &lt;strong&gt;&lt;code&gt;null&lt;/code&gt; is neither the same as &lt;code&gt;true&lt;/code&gt; nor as &lt;code&gt;false&lt;/code&gt;&lt;/strong&gt;. So the comparison has actually three possible results: &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt; or &lt;code&gt;none of them&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;... != false&lt;/code&gt; in this case means &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;none of them&lt;/code&gt;, whereas &lt;code&gt;... != true&lt;/code&gt; means only &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For more on equality in Kotlin, I recommend to read the official docs on &lt;a href="https://kotlinlang.org/docs/reference/equality.html"&gt;equality&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All in all, that wasn't my most brilliant moment for sure, but I thought others might stumble upon that as well. Hope you liked it.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>nullability</category>
    </item>
    <item>
      <title>Spring Library: Selenium-Docker pool</title>
      <dc:creator>Alexander Girke</dc:creator>
      <pubDate>Fri, 14 Aug 2020 11:03:44 +0000</pubDate>
      <link>https://dev.to/alxgrk/spring-library-selenium-docker-pool-4ja0</link>
      <guid>https://dev.to/alxgrk/spring-library-selenium-docker-pool-4ja0</guid>
      <description>&lt;p&gt;I recently started working on my Master's thesis. Until now, I haven't wrote a single line of text, but only code instead. And since I wanted to share my experiences I already made along the way, I decided to create a small library of a part of my work.&lt;/p&gt;

&lt;p&gt;There is no concrete title for the paper yet, but the main task is to model and automatize Twitter user behavior. To easily interact with Twitter, I decided to go for browser automation using Selenium. And since I wanted to embed this into an existing Spring application and needed to concurrently run different Selenium tasks, I wrote a module to provide Selenium-Docker containers.&lt;/p&gt;

&lt;h1&gt;
  
  
  The main concept 💌
&lt;/h1&gt;

&lt;p&gt;In general, I'm making use of the awesome library &lt;a href="https://github.com/testcontainers/testcontainers-java"&gt;Testcontainers&lt;/a&gt; to create the containers. Since we are talking about pools, it's all about taking containers and returning them to the pool after finishing work. The rest (environment cleanup, browser restart, ...) is done by the pool itself.&lt;/p&gt;

&lt;p&gt;When acquiring a container from the pool, it is also possible to specify a profile, which the underlying Chrome browser should run with. By this, you have the chance to access cookies and stuff stored from previous runs.&lt;/p&gt;

&lt;h1&gt;
  
  
  Dive into the pool 🤿
&lt;/h1&gt;

&lt;p&gt;Depending on your build tool, add the dependency to your project. For Maven it would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;de.alxgrk&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-selenium-pool-core&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.0.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Thanks to Spring auto-configuration, you are now able to get an &lt;code&gt;@Autowired&lt;/code&gt; instance of &lt;code&gt;WebDriverPool&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomeSeleniumTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt; &lt;span class="n"&gt;webDriverPool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;WebDriverPool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;WebDriverForContainer&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webDriverPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getWebDriverForContainer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;    

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Since it might happen, that all containers are in use, this method may return null. To circumvent this issue, you can also wait until there is a free container. &lt;em&gt;(If you've expected a suspending function at this point, have a look at &lt;a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/java.util.concurrent.-completion-stage/await.html"&gt;CompletionStage.await() extension function&lt;/a&gt; for converting &lt;code&gt;CompletableFuture&lt;/code&gt;)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomeSeleniumTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt; &lt;span class="n"&gt;webDriverPool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;WebDriverPool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;WebDriverForContainer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webDriverPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getWebDriverForContainerAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;// blocking get&lt;/span&gt;
        &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;// ... or with timeout&lt;/span&gt;
        &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SECONDS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;// ... or coroutine style&lt;/span&gt;
        &lt;span class="nc"&gt;GlobalScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;webDriverForContainer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;await&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;    

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  Doing the actual work 💪
&lt;/h1&gt;

&lt;p&gt;Now you are able to do whatever you wanted to do. And to not forget to return the container to the pool (unless you are sure, you'll need the exact same instance later), &lt;code&gt;WebDriverForContainer&lt;/code&gt; implements &lt;code&gt;AutoClosable&lt;/code&gt;. Means you can simply use &lt;code&gt;try-with-resources&lt;/code&gt; in Java or &lt;code&gt;.use()&lt;/code&gt; extension function in Kotlin.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomeSeleniumTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt; &lt;span class="n"&gt;webDriverPool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;WebDriverPool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;webDriverPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getWebDriverForContainer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webDriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://foo.bar/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;    

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  Configuration 🛠️
&lt;/h1&gt;

&lt;p&gt;Last but not least, it is possible to configure the pool via Spring-Boot properties. Auto-Completion when working with IntelliJ included. This is an example configuration showing the default values:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# default values
&lt;/span&gt;&lt;span class="py"&gt;selenium.pool.size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;
&lt;span class="py"&gt;selenium.pool.recording-directory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="c"&gt;# if empty, no recording will happen
&lt;/span&gt;&lt;span class="s"&gt;selenium.pool.profiles-directory= # if empty, a temporary directory will be created and deleted on exit&lt;/span&gt;
&lt;span class="py"&gt;selenium.pool.extension-files-in-classpath&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="c"&gt;# if empty, no extension files will be loaded
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  ✨ Bonus: Custom Chrome extensions
&lt;/h3&gt;

&lt;p&gt;If you read about the configuration properties carefully, you may have notice the parameter about extension files.&lt;br&gt;
It is possible to install a custom Chrome extension by specifying a &lt;code&gt;manifest.json&lt;/code&gt; and a corresponding &lt;code&gt;index.js&lt;/code&gt; file.&lt;br&gt;
This might be helpful for a couple of reasons including e.g. for grabbing the network traffic.&lt;br&gt;
For an example on how to use this, see &lt;code&gt;application.properties&lt;/code&gt;, &lt;code&gt;manifest.json&lt;/code&gt; and &lt;code&gt;index.js&lt;/code&gt; in &lt;a href="https://github.com/alxgrk/spring-selenium-pool/tree/master/spring-selenium-pool-example/src/main/resources/"&gt;spring-selenium-pool-example&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  Feedback
&lt;/h1&gt;

&lt;p&gt;I hope you like this library and find it useful. So please use it and tell me about it. I would be happy to hear what you like and what could be improved.&lt;/p&gt;

&lt;p&gt;Check out the library on Github (a working example can be found in &lt;code&gt;spring-selenium-pool-example&lt;/code&gt;) and leave a star if you like it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/alxgrk"&gt;
        alxgrk
      &lt;/a&gt; / &lt;a href="https://github.com/alxgrk/spring-selenium-pool"&gt;
        spring-selenium-pool
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A library that provides a pool of dockerized Selenium instances.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
🏊 Selenium Pool&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://search.maven.org/search?q=g:%22de.alxgrk%22%20AND%20a:%22spring-selenium-pool-core%22" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/a7e4cd4225448574c6f1423f1efc440ed452d16fca2650ff86152e6ea6261e7a/68747470733a2f2f696d672e736869656c64732e696f2f6d6176656e2d63656e7472616c2f762f64652e616c7867726b2f737072696e672d73656c656e69756d2d706f6f6c2d636f72653f636f6c6f723d253233303830267374796c653d666f722d7468652d6261646765" alt="Maven Central"&gt;&lt;/a&gt;
&lt;a href="https://dev.to/alxgrk/spring-library-selenium-docker-pool-4ja0" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/1f3c6413af566c3bdc34d592cb5f299bf014242798daf4854b3c531ad522b904/68747470733a2f2f6432666c746978307632653073622e636c6f756466726f6e742e6e65742f6465762d62616467652e737667" alt="DEV.to Post" height="30" width="30"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This small library helps to create a pool of Selenium Docker Container with a configurable size.&lt;/p&gt;
&lt;h2&gt;
🩲 Prerequisites&lt;/h2&gt;
&lt;p&gt;Make sure to have the following installed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
💿 Installation&lt;/h2&gt;
&lt;p&gt;For Maven, add this dependency:&lt;/p&gt;
&lt;div class="highlight highlight-text-xml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&amp;lt;&lt;span class="pl-ent"&gt;dependency&lt;/span&gt;&amp;gt;
  &amp;lt;&lt;span class="pl-ent"&gt;groupId&lt;/span&gt;&amp;gt;de.alxgrk&amp;lt;/&lt;span class="pl-ent"&gt;groupId&lt;/span&gt;&amp;gt;
  &amp;lt;&lt;span class="pl-ent"&gt;artifactId&lt;/span&gt;&amp;gt;spring-selenium-pool-core&amp;lt;/&lt;span class="pl-ent"&gt;artifactId&lt;/span&gt;&amp;gt;
  &amp;lt;&lt;span class="pl-ent"&gt;version&lt;/span&gt;&amp;gt;1.0.0&amp;lt;/&lt;span class="pl-ent"&gt;version&lt;/span&gt;&amp;gt;
&amp;lt;/&lt;span class="pl-ent"&gt;dependency&lt;/span&gt;&amp;gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;For Gradle use:&lt;/p&gt;
&lt;div class="highlight highlight-source-kotlin notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;implementation(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;de.alxgrk:spring-selenium-pool-core:1.0.0&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;)&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
🛠️ Configuration&lt;/h2&gt;
&lt;p&gt;This library is configurable via Spring-Boot properties. Auto-Completion when working with IntelliJ included.
This is an example configuration showing the default values:&lt;/p&gt;
&lt;div class="highlight highlight-source-ini notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; default values&lt;/span&gt;
&lt;span class="pl-k"&gt;selenium.pool.size&lt;/span&gt;=3
&lt;span class="pl-k"&gt;selenium.pool.recording-directory&lt;/span&gt;= &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; if empty, no recording will happen&lt;/span&gt;
&lt;span class="pl-k"&gt;selenium.pool.profiles-directory&lt;/span&gt;= &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; if empty, a temporary directory will be created and deleted on exit&lt;/span&gt;
&lt;span class="pl-k"&gt;selenium.pool.extension-files-in-classpath&lt;/span&gt;= &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; if empty, no extension files will be loaded&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
🥽 Utilities&lt;/h2&gt;
&lt;p&gt;To connect to the running Selenium container, you have to have &lt;code&gt;vncviewer&lt;/code&gt; &amp;amp; &lt;code&gt;tigervnc-common&lt;/code&gt; installed. If correctly configured, simply…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/alxgrk/spring-selenium-pool"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>kotlin</category>
      <category>selenium</category>
      <category>spring</category>
    </item>
  </channel>
</rss>
