Build a Markdown-based Blog with Spring Boot - Part 4
Tags: java, springboot, thymeleaf, markdown
Published: 2021-05-27 09:51:41

<p>Before continuing with part 4 of this series, make sure you have checked out the previous three parts as well:&nbsp;<a href="https://www.roshanadhikary.com.np/2021/05/build-a-markdown-based-blog-with-spring-boot-part-1.html">Part 1</a> | <a href="https://www.roshanadhikary.com.np/2021/05/build-a-markdown-based-blog-with-spring-boot-part-2.html">Part 2</a> | <a href="https://www.roshanadhikary.com.np/2021/05/build-a-markdown-based-blog-with-spring-boot-part-3.html">Part 3</a></p><p>Up to this point, we have a database ready, and we have established conventions for how and where we store our Markdown files. These Markdown files will be parsed, line by line, and rendered as HTML string before we persist it in the database. For parsing and rendering, we have already defined some utility classes -- <em>MdFileReader</em> and <em>MdToHtmlRenderer</em>.</p><p>For this session, we will write a class that implements the <em>ApplicationListener</em> interface such that on every firing of the&nbsp;<em>ContextRefreshedEvent</em>, our event-listener class' <em>onApplicationEvent</em> method is invoked. Within the onApplicationEvent method, we look for new Markdown files, and if any such new file exists, we persist it in the database.</p><p>But before that, we need to add another dependency -- <em>jsoup</em>.</p><h3 style="text-align: left;">Adding jsoup dependency</h3><p>Remember how we have a <em>synopsis</em> attribute in the <em>Post</em> entity? We will use that attribute (or field, in the database) and set it to the first 150 characters of the actual post's body. However, after parsing the Markdown and rendering HTML from it, the post's body would look like, say,</p>
<pre>&lt;h1&gt;Hello World!&lt;/h1&gt;&lt;br /&gt;&lt;p&gt;This is the post's actual body, rendered in HTML!&lt;/p&gt;
</pre>
<p>As you can imagine, most of the rendered text is actually HTML elements and symbols. Hence, we need to select the first 150 characters from the actual text, excluding the HTML elements and symbols.</p><p>We will need <em>jsoup</em> for doing exactly that. To add <em>jsoup</em> as one of our dependencies, make sure to add the following segment to your <em>pom.xml</em>&nbsp;file.</p>

<p>
  {% gist https://gist.github.com/roshanadh/115c2f18efad7dc12b6cb8bf9f0574cf.js %}
</p>

<p>Like before, load the Maven changes in your POM file.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-uHbHS_zq4f4/YK9HHKIP2bI/AAAAAAAAG-M/3iJYRh9sVcM2_WufrdrKtBS8zDsT7RNsQCLcBGAsYHQ/s1358/add-jsoup.png" style="margin-left: 1em; margin-right: 1em;"><img alt="Loading Maven changes after adding jsoup dependency" border="0" data-original-height="671" data-original-width="1358" src="https://1.bp.blogspot.com/-uHbHS_zq4f4/YK9HHKIP2bI/AAAAAAAAG-M/3iJYRh9sVcM2_WufrdrKtBS8zDsT7RNsQCLcBGAsYHQ/s16000/add-jsoup.png" title="Loading Maven changes after adding jsoup dependency" /></a></div><p>Then, we can proceed to our event-listener class.</p><h3 style="text-align: left;">ContextEventListener class</h3><p style="text-align: left;">Begin by creating the class that implements <em>ApplicationListener</em> interface that takes in as type parameter the <em>ContextReferencedEvent</em> type. With this, our class would need to override the <em>onApplicationEvent</em> method.</p><p style="text-align: left;">We also need some instance variables for purposes that will be apparent soon.</p>

<p>
  {% gist https://gist.github.com/roshanadh/0b59d29d85ba0339ae5601bc78860e94.js %}
</p>

<p style="text-align: left;">With <em>@Value</em>, we inject into the <em>postFiles</em>, an array of&nbsp;<em>Resource</em> type, any files that exist in the classpath inside <em>posts</em> directory.</p><p style="text-align: left;">Next, we can implement our <em>onApplicationEvent</em> method.</p>
<p>
  {% gist https://gist.github.com/roshanadh/687b271670fa033fede560e96740b294.js %}
</p>
<p style="text-align: left;">In this method, we start by iterating over each file in the <em>postFiles</em> array. We check to see if a post with the same ID as the file exists in the database. If it does not, we persist the post in the database. Before saving the post, we set it's attributes.</p><p style="text-align: left;">Notice how we are using static utility methods from <em>PostUtil</em> and <em>AuthorUtil</em> classes for operations concerning posts and authors respectively.</p><h3 style="text-align: left;">PostUtil and AuthorUtil classes</h3>
<p>
  {% gist https://gist.github.com/roshanadh/01fce6274565054ff903f303decb4458.js %}
</p>
<p style="text-align: left;">In the <em>PostUtil</em> class, we define methods: <em>getHtmlContentFromMdLines</em> and <em>getSynopsisFromHtmlContent</em>.</p><p style="text-align: left;">The <em>getHtmlContentFromMdLines</em>&nbsp;returns a <em>String</em>&nbsp;of HTML content rendered using the <em>List</em>&nbsp;of Markdown lines passed as argument.</p><p style="text-align: left;">The <em>getSynopsisFromHtmlContent</em>&nbsp;method returns the first 150 characters of the text content parsed from the HTML content passed as argument. If the text content is fewer than 150 characters in length, the entire <em>String</em> is returned.&nbsp;</p>
<p>
  {% gist https://gist.github.com/roshanadh/e5515362769a0678ca0b41e1d2a00009.js %}
</p>
<p style="text-align: left;">In the <em>AuthorUtil</em> class, we define method: <em>bootstrapAuthor</em>. If no author exists in the database, it creates a new author, before persisting it in the database, and returns it. Otherwise, it returns the first author that exists in the database.</p><h3 style="text-align: left;">Get it running</h3><p style="text-align: left;">Now, when we run our Spring Boot application, or whenever we cause the Spring Context to refresh, a <em>ContextRefreshedEvent</em> is fired. This looks for a new blog post in the <em>resources/posts/</em> directory and persists it.</p><p style="text-align: left;">To test this, let us create a <em>posts</em> directory inside the <em>resources</em> directory.</p><p style="text-align: left;">Inside it, I will create a new file, <b style="font-style: italic;">1_Hello_World!.md</b>, with the following content.</p>
<pre># Hello World!
This is my *first* blog post. &lt;br&gt;
Be sure to read future parts of this blog post series, &lt;br&gt;
titled **Build a Markdown-based Blog with Spring Boot**.
</pre>
<p style="text-align: left;">Then, let's run our Spring Boot application. After the JVM is up and running, we can check our database to see that our new blog post has been persisted successfully, along with the author.</p><h3 style="text-align: left;">Code</h3><p style="text-align: left;">We have now created an application that persists new blog posts to our database. Now, we will work towards displaying blog posts using the <em>Thymeleaf</em> template engine. But that's for the next post.</p><p style="text-align: left;">The GitHub repository has been updated with <a href="https://github.com/roshanadh/markdown-blog/tree/part-4" rel="nofollow" target="_blank">this session's code</a>, do check it out. Or if you need to check <a href="https://github.com/roshanadh/markdown-blog/branches/all" rel="nofollow" target="_blank">previous sessions' code</a>, please do so as well.&nbsp;</p>