<?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: Adwait Thattey</title>
    <description>The latest articles on DEV Community by Adwait Thattey (@adwaitthattey).</description>
    <link>https://dev.to/adwaitthattey</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%2F254922%2F1bf697bd-a61e-4cf4-851b-dd7a6c6e4071.png</url>
      <title>DEV Community: Adwait Thattey</title>
      <link>https://dev.to/adwaitthattey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adwaitthattey"/>
    <language>en</language>
    <item>
      <title>Optimizing C++ vector for 2x performance !</title>
      <dc:creator>Adwait Thattey</dc:creator>
      <pubDate>Mon, 01 Aug 2022 06:08:00 +0000</pubDate>
      <link>https://dev.to/adwaitthattey/optimizing-c-vector-for-2x-performance--1691</link>
      <guid>https://dev.to/adwaitthattey/optimizing-c-vector-for-2x-performance--1691</guid>
      <description>&lt;p&gt;&lt;strong&gt;std::vector&lt;/strong&gt; is perhaps one of the most used data structures in C++. It provides a dynamic array that is very easy to use. &lt;br&gt;
But the simplicity comes at a slight performance cost that is often ignored. It is however really simple to optimize this data-structure and get a performance boost. &lt;/p&gt;

&lt;p&gt;First lets look at the problem. &lt;/p&gt;

&lt;p&gt;Here I am creating a simple class that keeps track of the constructor, destructor and the copy constructor calls. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sample&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;

&lt;span class="n"&gt;Sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;m_constructor_count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="n"&gt;m_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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



&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;Sample&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;m_destructor_count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;span class="n"&gt;Sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Sample&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;m_copy_constructor_count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



   &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;m_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;m_constructor_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m_destructor_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m_copy_constructor_count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;



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



&lt;span class="c1"&gt;// initialize counts to 0&lt;/span&gt;

&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;Sample&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;m_constructor_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;Sample&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;m_destructor_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;Sample&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;m_copy_constructor_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;Now lets create a vector , add 3 elements to it and print the calls&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

std::vector&amp;lt;Sample&amp;gt; vec;

vec.push_back( Sample(1) );
vec.push_back( Sample(2) );
vec.push_back( Sample(3) );


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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiou9kyvebprsohic9nk7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiou9kyvebprsohic9nk7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have &lt;strong&gt;3 Constructor&lt;/strong&gt; calls but &lt;strong&gt;6 Copy Calls&lt;/strong&gt; and &lt;strong&gt;9 Destructor&lt;/strong&gt; calls !&lt;/p&gt;

&lt;p&gt;Constructor calls are expected, we are creating 3 elements. But where are these copy and destructor calls coming from ??&lt;/p&gt;

&lt;p&gt;Well, the destructor calls are just a sum of constructor and copy. So lets look at the copy calls.&lt;/p&gt;

&lt;p&gt;This is due to 2 problems with std::vector&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Copy while insertion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Copy while resizing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lets look at 1st problem. &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Copy while insertion
&lt;/h3&gt;

&lt;p&gt;Look at this statement&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;vec.push_back( Sample(1) );&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here we are first creating an object of class Sample, then when it is inserted to the vector, it is copied to the vector's memory. &lt;/p&gt;

&lt;p&gt;This is the source of 3 copy calls as we have 3 items. &lt;/p&gt;

&lt;p&gt;Since we just want to insert element into vector, is there any way to directly create the object in vector's memory?&lt;/p&gt;

&lt;p&gt;Yes there is!&lt;/p&gt;

&lt;p&gt;std::vector provides another method &lt;strong&gt;&lt;a href="https://en.cppreference.com/w/cpp/container/vector/emplace_back" rel="noopener noreferrer"&gt;emplace_back&lt;/a&gt;&lt;/strong&gt; .&lt;/p&gt;

&lt;p&gt;Using this method, we can directly pass the arguments that we would have passed to the constructor and the vector directly creates the object in its memory&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;vec.emplace_back( 1 );&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lets look at the results now&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj86u15ci3ylwgxs3yvi1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj86u15ci3ylwgxs3yvi1.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have reduced 3 copy calls &lt;/p&gt;

&lt;p&gt;What about the remaining 3 calls?&lt;/p&gt;

&lt;p&gt;This brings us to the 2nd problem&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Copy while resizing
&lt;/h3&gt;

&lt;p&gt;A vector is a dynamic array. Meaning, the compiler doesn't know about it's size beforehand. &lt;/p&gt;

&lt;p&gt;So the compiler starts with 1 and dynamically increases the capacity of vector as we add to it. &lt;/p&gt;

&lt;p&gt;This means that every time the vector runs out of it's capacity, it needs to resize. This resize operation copies all the elements from 1 memory location to another. &lt;/p&gt;

&lt;p&gt;Lets track how the 3 copies are happening&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;

&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Sample&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// initialized with capacity 1&lt;/span&gt;



&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Sample&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="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// insert 1 element. capacity is 1, size is 1. &lt;/span&gt;



&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// exceeded capacity. Resize operation. Copy the 1 element in the vector to new location. New size and capacity is 2&lt;/span&gt;

&lt;span class="c1"&gt;// 1 Copy&lt;/span&gt;


&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// exceeded size. Resize vector. Copy the 2 elements in the vector to new location. New size is 3&lt;/span&gt;

&lt;span class="c1"&gt;// 2 Copies&lt;/span&gt;



&lt;span class="n"&gt;Total&lt;/span&gt; &lt;span class="n"&gt;copies&lt;/span&gt; &lt;span class="n"&gt;due&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;resize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;This is the source of 3 additional copy calls. &lt;/p&gt;

&lt;p&gt;Does this mean that n copies will take place every time we insert a new element?&lt;/p&gt;

&lt;p&gt;Well no. The system can allocate additional memory assuming that we are adding more elements. &lt;/p&gt;

&lt;p&gt;While this can change compiler to compiler, memory is usually allocated in 2s power. &lt;/p&gt;

&lt;p&gt;For example, when we add 3 elements, memory is allocated for 4, adding 5th allocates for 8, adding 9th allocates for 16, adding 17th for 32 and so on. Take a look at this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fck384l0bze5dc8lzp4tg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fck384l0bze5dc8lzp4tg.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means that not only are we causing unnecessary copy calls, we are also &lt;strong&gt;potentially using up additional memory that is not needed&lt;/strong&gt;. For example in the above case, even if we had only &lt;strong&gt;129&lt;/strong&gt; elements, we were using the memory for &lt;strong&gt;256&lt;/strong&gt; elements. &lt;/p&gt;

&lt;p&gt;So, is there a way to avoid this?&lt;/p&gt;

&lt;p&gt;Yes there is. &lt;/p&gt;

&lt;p&gt;We just need a way to tell the compiler how many elements we are planning to insert so that system can reserve the needed memory beforehand reducing the copy calls.&lt;/p&gt;

&lt;p&gt;For this, &lt;strong&gt;std::vector&lt;/strong&gt; provides another method. &lt;a href="https://en.cppreference.com/w/cpp/container/vector/reserve" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;em&gt;reserve&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This allows us to specify how many elements we are planning to insert and causes the vector to allocate that much memory beforehand&lt;/p&gt;

&lt;p&gt;Lets modify our code to use this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;

&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Sample&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reserve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// specify we are going to insert 3 elements&lt;/span&gt;



&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emplace_back&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="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emplace_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emplace_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fboue8zo4f55v2alpuu2z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fboue8zo4f55v2alpuu2z.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have successfully removed all the copy constructor calls. &lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Improvement
&lt;/h3&gt;

&lt;p&gt;Now as a final step, lets also see how much performance improvement can this lead to. &lt;/p&gt;

&lt;p&gt;For this I am going to count the number of clock ticks utilized for inserting 10 million elements&lt;/p&gt;

&lt;p&gt;First without using emplace and reserve&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxg6jpsiawkc9uvjyspn5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxg6jpsiawkc9uvjyspn5.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;732483 ticks&lt;/p&gt;

&lt;p&gt;(Also notice we have 26 million additional copy calls for just 10 million elements!)&lt;/p&gt;

&lt;p&gt;Now using emplace and reserve &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feoorl3xdgypn7sqbzzy7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feoorl3xdgypn7sqbzzy7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;386635 ticks&lt;/p&gt;

&lt;p&gt;That's more than 2x performance improvement. And best part , it did not require a lot of effort. &lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;A copy operation takes place when we use push_back to insert into vector and multiple copy operations take place whenever the vector runs out of space and resize takes place. &lt;/p&gt;

&lt;p&gt;Use emplace_back instead of push_back wherever possible. &lt;/p&gt;

&lt;p&gt;If the number of elements we are going to insert is known before hand, use reserve to specify the capacity. &lt;/p&gt;

&lt;p&gt;Even if exact size is not known, it might still be worthwhile to use reserve with some known lower bound for the number of elements. &lt;/p&gt;

&lt;h3&gt;
  
  
  Credits
&lt;/h3&gt;

&lt;p&gt;This video by "The Cherno" on Youtube was the source of inspiration for this post. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;&lt;a href="https://www.youtube.com/watch?v=HcESuwmlHEY" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=HcESuwmlHEY&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This channel has lots of neat tips and tricks for C++. Please like this video and subscribe to his channel.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/c/TheChernoProject" rel="noopener noreferrer"&gt;The Cherno - YouTube &lt;/a&gt;&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>optimization</category>
      <category>programming</category>
      <category>stl</category>
    </item>
    <item>
      <title>Building an Event Listener SPI (Plugin) for KeyCloak</title>
      <dc:creator>Adwait Thattey</dc:creator>
      <pubDate>Fri, 20 Dec 2019 15:30:09 +0000</pubDate>
      <link>https://dev.to/adwaitthattey/building-an-event-listener-spi-plugin-for-keycloak-2044</link>
      <guid>https://dev.to/adwaitthattey/building-an-event-listener-spi-plugin-for-keycloak-2044</guid>
      <description>&lt;p&gt;In this blog, I will talk about how to build an event listener plugin (called an SPI) for KeyCloak&lt;/p&gt;

&lt;p&gt;So, what is &lt;strong&gt;Keycloak&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keycloak&lt;/strong&gt; is an Open Source Identity and Access Management Framework built by &lt;strong&gt;RedHat&lt;/strong&gt;. It provides a lot of advanced features like SSO, Social Auth, support for multiple auth protocols, etc. Read more here: &lt;a href="https://www.keycloak.org/" rel="noopener noreferrer"&gt;https://www.keycloak.org/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But one of the most important features is the ability to extend any functionality of KeyCloak by simply building a plugin.  &lt;/p&gt;

&lt;p&gt;During my internship this summer, we needed to log all the events of users (and admins) happening within KeyCloak and send them to external systems for analysis. This is needed in many situations. &lt;em&gt;(one example is if you are using an external &lt;a href="https://en.wikipedia.org/wiki/Security_information_and_event_management" rel="noopener noreferrer"&gt;SIEM&lt;/a&gt; to log and analyze incidents)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;By default, KeyCloak logs don't contain user/admin events. And even if we enable that, it would be difficult to build an external system which monitors and parses the logs to extract required events. &lt;strong&gt;Instead, we can build a plugin for KeyCloak to hook into the system and do &lt;em&gt;"something"&lt;/em&gt; whenever an event occurs &lt;em&gt;(In our case, fire external API calls)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So, let's build one :)  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note&lt;/em&gt;&lt;/strong&gt;: The entire code for the event listener is available here. &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/adwait-thattey" rel="noopener noreferrer"&gt;
        adwait-thattey
      &lt;/a&gt; / &lt;a href="https://github.com/adwait-thattey/keycloak-event-listener-spi" rel="noopener noreferrer"&gt;
        keycloak-event-listener-spi
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A sample event listener SPI for keycloak
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;I would be using &lt;a href="https://maven.apache.org/" rel="noopener noreferrer"&gt;Maven&lt;/a&gt; here for managing dependencies and building project.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So let's get the &lt;a href="https://github.com/adwait-thattey/keycloak-event-listener-spi/blob/master/sample_event_listener/pom.xml" rel="noopener noreferrer"&gt;pom.xml&lt;/a&gt; sorted out first.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(If you are not familiar with Maven, we use a &lt;strong&gt;pom.xml&lt;/strong&gt; file in Maven to list all the project details including all the dependencies)&lt;/em&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;em&gt;(if the above gist is not visible, you can find the file &lt;a href="https://github.com/adwait-thattey/keycloak-event-listener-spi/blob/master/sample_event_listener/pom.xml" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;pom.xml&lt;/code&gt;, we define the parent details, project name &lt;code&gt;Sample Event Listener&lt;/code&gt;), version, artifact-id (here &lt;code&gt;sample_event_listener&lt;/code&gt;), dependencies and build configuration. &lt;/p&gt;




&lt;p&gt;The next step is to implement the SPI. For this, we need to implement 2 classes. &lt;code&gt;Provider&lt;/code&gt; and &lt;code&gt;ProviderFactory&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;so let's create our package in &lt;code&gt;src/main/java&lt;/code&gt;. &lt;br&gt;
Here the package name is &lt;code&gt;com.coderdude.sampleeventlistenerprovider.provider&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;coderdude&lt;/code&gt; :  because my dev alias is coderdude :D&lt;br&gt;&lt;br&gt;
&lt;code&gt;sampleeventlistenerprovider&lt;/code&gt;: Could be shorter but let's leave it at that&lt;br&gt;&lt;br&gt;
&lt;code&gt;provider&lt;/code&gt;: The last provider is there because there can potentially be other modules that you use in your provider.&lt;/p&gt;

&lt;p&gt;Now this package is going to contain the 2 above discussed classes.&lt;br&gt;
The &lt;code&gt;Provider&lt;/code&gt; class contains the actual logic of the plugin. The &lt;code&gt;ProviderFactory&lt;/code&gt; is a wrapper that initializes the provider. &lt;strong&gt;The difference is important&lt;/strong&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Factory&lt;/code&gt; is initialized only when KeyCloak is started. A new instance of &lt;code&gt;Provider&lt;/code&gt; is created by &lt;code&gt;Factory&lt;/code&gt; every time required. &lt;em&gt;(In our case every time an event occurs)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Only 1 instance of &lt;code&gt;Factory&lt;/code&gt; will exist. Multiple providers can exist at the same time (say 2 events occur at the same time). &lt;/li&gt;
&lt;li&gt;Providers are destroyed as soon as they complete their tasks. The Factory exists as long as KeyCloak is running. &lt;/li&gt;
&lt;li&gt;Any error in Factory will crash KeyCloak. An error in Provider will simply go to the logs and rest of Keycloak will function normally
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So let's start by creating a Provider. &lt;br&gt;
The name of the class will be &lt;code&gt;SampleEventListenerProvider&lt;/code&gt; which implements the &lt;code&gt;EventListenerProvider&lt;/code&gt; interface &lt;em&gt;(This interface is provided by KeyCloak)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.coderdude.sampleeventlistenerprovider.provider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.keycloak.events.Event&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.keycloak.events.EventListenerProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.keycloak.events.admin.AdminEvent&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Map&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;


&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SampleEventListenerProvider&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;EventListenerProvider&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;SampleEventListenerProvider&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Keep these imports for now. We will need them.&lt;/em&gt;&lt;br&gt;
So here, we are just going to print all the events to the console. All events are provided by 2 classes: &lt;code&gt;org.keycloak.events.Event&lt;/code&gt; and &lt;code&gt;org.keycloak.events.admin.AdminEvent&lt;/code&gt; &lt;br&gt;
The normal events occur whenever a normal user does something. Admin events occur when administrators do something.&lt;br&gt;&lt;br&gt;
We need to write appropriate methods to convert these class objects to readable strings. &lt;br&gt;
Here is the method to build string for an &lt;code&gt;Event&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We are capturing all the parameters, errors and details. (hence the map, because the details is an array)&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Event&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;StringBuilder&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StringBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;


        &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"type="&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getType&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", realmId="&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRealmId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", clientId="&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getClientId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", userId="&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUserId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", ipAddress="&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getIpAddress&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;


        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getError&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

            &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", error="&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getError&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

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


        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDetails&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDetails&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;entrySet&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

                &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

                &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getKey&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;indexOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;' '&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

                    &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"="&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

                    &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

                &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

                    &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"='"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

                    &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

                    &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"'"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

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

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

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


        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;Of course, this is a very naive implementation. What we actually did was define methods to wrap these events in other objects and make API calls to external systems. But this will work for now. &lt;br&gt;
We can build a similar method for &lt;code&gt;AdminEvent&lt;/code&gt;. You will find it in the main full code.&lt;/p&gt;

&lt;p&gt;Once this is done, we need to override 2 methods provided by the &lt;br&gt;
&lt;code&gt;EventListenerProvider&lt;/code&gt; interface. These are &lt;code&gt;onEvent&lt;/code&gt; and &lt;code&gt;close&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
Here it is&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onEvent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Event&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Event Occurred:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onEvent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AdminEvent&lt;/span&gt; &lt;span class="n"&gt;adminEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Admin Event Occurred:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;adminEvent&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;onEvent&lt;/code&gt; is the actual method called whenever an event occurs. We need to overload onEvent twice to capture both &lt;code&gt;Event&lt;/code&gt; and &lt;code&gt;AdminEvent&lt;/code&gt;. &lt;br&gt;
Finally, the &lt;code&gt;close&lt;/code&gt; method is called just before the class is destroyed. Sort of like a destructor. We need to override it even if we don't need to use it.&lt;/p&gt;

&lt;p&gt;You can find the full class code (along with string implementation for AdminEvent) &lt;a href="https://github.com/adwait-thattey/keycloak-event-listener-spi/blob/master/sample_event_listener/src/main/java/com/coderdude/sampleeventlistenerprovider/provider/SampleEventListenerProvider.java" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Next step is to implement the &lt;code&gt;ProviderFactory&lt;/code&gt;&lt;br&gt;
The name of the class is  &lt;code&gt;SampleEventListenerProviderFactory&lt;/code&gt; which implements &lt;code&gt;EventListenerProviderFactory&lt;/code&gt;&lt;br&gt;&lt;br&gt;
Here is the code: &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(if the above gist is not visible, you can find the file &lt;a href="https://github.com/adwait-thattey/keycloak-event-listener-spi/blob/master/sample_event_listener/src/main/java/com/coderdude/sampleeventlistenerprovider/provider/SampleEventListenerProviderFactory.java" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We override multiple methods here. The main ones are the &lt;code&gt;create&lt;/code&gt; and &lt;code&gt;getId&lt;/code&gt;. The create method should initialize and return an instance of provider (in our case &lt;code&gt;SampleEventListenerProvider&lt;/code&gt;). The &lt;code&gt;getId&lt;/code&gt; should return a string with the name of the plugin&lt;/p&gt;




&lt;p&gt;The next and the final task is to provide a link to our class. For this we need to create resources. &lt;br&gt;
create a folder named &lt;code&gt;resources&lt;/code&gt; in &lt;code&gt;src/main&lt;/code&gt; (alongside &lt;code&gt;java&lt;/code&gt; folder) &lt;br&gt;
Now create the following file in &lt;code&gt;resources/META-INF/services/&lt;/code&gt; named &lt;code&gt;org.keycloak.events.EventListenerProviderFactory&lt;/code&gt; . Note that full path to location of file is &lt;code&gt;src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;This file just contains one line with the package and name of our factory class&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;coderdude&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sampleeventlistenerprovider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SampleEventListenerProviderFactory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's it. We have written the plugin. Now let's build and package it.&lt;br&gt;
&lt;em&gt;I have used maven to build and package&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once packaging is complete, you should see the &lt;code&gt;jar&lt;/code&gt; and &lt;code&gt;sources&lt;/code&gt; in the target directory &lt;br&gt;
Here is my final directory structure&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fm2ku1yozov524hdv4qt4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fm2ku1yozov524hdv4qt4.png" alt="Directory Structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will only need the &lt;code&gt;sample-event-listener.jar&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;-- &lt;br&gt;
Now it's time to deploy the plugin to KeyCloak.&lt;/p&gt;

&lt;p&gt;Let's get setup with a KeyCloak first. You will find the getting started guide here &lt;a href="https://www.keycloak.org/docs/latest/getting_started/index.html" rel="noopener noreferrer"&gt;https://www.keycloak.org/docs/latest/getting_started/index.html&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Quickly download and create an admin user and login to KeyCloak. &lt;br&gt;
Now let's create a new realm named &lt;code&gt;newrealm&lt;/code&gt; and add a user named &lt;code&gt;newuser001&lt;/code&gt; in the new realm.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadwait-thattey.github.io%2Fassets%2Fblogs%2Fkeycloak_plugin%2FScreenshot_20191219_191638.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadwait-thattey.github.io%2Fassets%2Fblogs%2Fkeycloak_plugin%2FScreenshot_20191219_191638.png" alt="Create new User Snap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's also create a password for this new user&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadwait-thattey.github.io%2Fassets%2Fblogs%2Fkeycloak_plugin%2FScreenshot_20191219_191654.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadwait-thattey.github.io%2Fassets%2Fblogs%2Fkeycloak_plugin%2FScreenshot_20191219_191654.png" alt="Set New User password"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's time to deploy our awesome plugin&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The deployment process is pretty straightforward. We need to copy the &lt;code&gt;sample-event-listener.jar&lt;/code&gt; to &lt;code&gt;$KEYCLOAK_DIR/standalone/deployments/&lt;/code&gt; where $KEYCLOAK_DIR is the main KeyCloak directory (after unzipping)&lt;/p&gt;

&lt;p&gt;KeyCloak supports hot-reloading. So as soon we copy the jar file, keycloak should reload and deploy the plugin. But just to be sure, let's restart the Keycloak server.&lt;/p&gt;

&lt;p&gt;You should see a line like this&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deployed "sample-event-listener.jar" (runtime-name : "sample-event-listener.jar")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadwait-thattey.github.io%2Fassets%2Fblogs%2Fkeycloak_plugin%2FScreenshot_20191220_170051.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadwait-thattey.github.io%2Fassets%2Fblogs%2Fkeycloak_plugin%2FScreenshot_20191220_170051.png" alt="Set New User password"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Now we need to allow this plugin to listen to events.&lt;br&gt;
Go to &lt;code&gt;newrealm-&amp;gt;manage-&amp;gt;events-&amp;gt;config&lt;/code&gt; or this url &lt;code&gt;/auth/admin/master/console/#/realms/newrealm/events-settings&lt;/code&gt; &lt;strong&gt;Make sure to replace newrealm with the name of the realm you created&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the config, event-listeners, add &lt;code&gt;sample_event_listener&lt;/code&gt; to the list and hit save.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadwait-thattey.github.io%2Fassets%2Fblogs%2Fkeycloak_plugin%2FScreenshot_20191220_170208.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadwait-thattey.github.io%2Fassets%2Fblogs%2Fkeycloak_plugin%2FScreenshot_20191220_170208.png" alt="Set New User password"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now our plugin should be able to capture all events.&lt;/p&gt;



&lt;p&gt;Lets test this&lt;/p&gt;

&lt;p&gt;Login to the newrealm using the user that was created above.&lt;/p&gt;

&lt;p&gt;You should see an event occuring in the console&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;17:03:01,797 INFO  [stdout] (default task-5) Event Occurred:type=LOGIN, realmId=newrealm, clientId=account, userId=efc09972-6166-4ed6-9ca0-15c030e47f54, ipAddress=127.0.0.1, auth_method=openid-connect, auth_type=code, redirect_uri=http://localhost:8180/auth/realms/newrealm/account/login-redirect, consent=no_consent_required, code_id=78db58ed-3c99-4d42-aced-b69873c59f12, username=newuser001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Logout should also be captured&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;17:03:51,211 INFO  [stdout] (default task-5) Event Occurred:type=LOGOUT, realmId=newrealm, clientId=null, userId=efc09972-6166-4ed6-9ca0-15c030e47f54, ipAddress=127.0.0.1, redirect_uri=http://localhost:8180/auth/realms/newrealm/account/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Trying to login with incorrect password is also captured (because we were also capturing errors)&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;17:04:04,505 WARN  [org.keycloak.events] (default task-5) type=LOGIN_ERROR, realmId=master, clientId=security-admin-console, userId=null, ipAddress=127.0.0.1, error=user_not_found, auth_method=openid-connect, auth_type=code, redirect_uri=http://localhost:8180/auth/admin/master/console/#/realms/newrealm/users, code_id=59a85ee0-a8f6-4fad-8667-f72de2da18fd, username=newuser001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Looks like this in my console&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadwait-thattey.github.io%2Fassets%2Fblogs%2Fkeycloak_plugin%2FScreenshot_20191220_170423.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadwait-thattey.github.io%2Fassets%2Fblogs%2Fkeycloak_plugin%2FScreenshot_20191220_170423.png" alt="Set New User password"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! Our plugin is able to capture events&lt;/p&gt;



&lt;p&gt;Wrapping Up:&lt;/p&gt;

&lt;p&gt;Once again, the entire code is available here:&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://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/adwait-thattey" rel="noopener noreferrer"&gt;
        adwait-thattey
      &lt;/a&gt; / &lt;a href="https://github.com/adwait-thattey/keycloak-event-listener-spi" rel="noopener noreferrer"&gt;
        keycloak-event-listener-spi
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A sample event listener SPI for keycloak
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;This is a very basic example. We can do lots more. There is a lot more useful information that keycloak events provide that can be captured. Like current realm, ip address of the person trying to login, access token IDs if it is an api login, etc.&lt;/p&gt;




&lt;p&gt;If you liked this blog, hit like :)&lt;/p&gt;

&lt;p&gt;Bye!&lt;/p&gt;

&lt;p&gt;Adwait Thattey,&lt;br&gt;&lt;br&gt;
&lt;a href="https://adwait-thattey.github.io/" rel="noopener noreferrer"&gt;https://adwait-thattey.github.io/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>keycloak</category>
      <category>computerscience</category>
    </item>
  </channel>
</rss>
