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 -- MdFileReader and MdToHtmlRenderer.
For this session, we will write a class that implements the ApplicationListener interface such that on every firing of the ContextRefreshedEvent, our event-listener class' onApplicationEvent 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.
But before that, we need to add another dependency -- jsoup.
Adding jsoup dependency
Remember how we have a synopsis attribute in the Post 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,
<h1>Hello World!</h1><br /><p>This is the post's actual body, rendered in HTML!</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.
We will need jsoup for doing exactly that. To add jsoup as one of our dependencies, make sure to add the following segment to your pom.xml file.
Like before, load the Maven changes in your POM file.
Then, we can proceed to our event-listener class.
Begin by creating the class that implements ApplicationListener interface that takes in as type parameter the ContextReferencedEvent type. With this, our class would need to override the onApplicationEvent method.
We also need some instance variables for purposes that will be apparent soon.
With @Value, we inject into the postFiles, an array of Resource type, any files that exist in the classpath inside posts directory.
Next, we can implement our onApplicationEvent method.
In this method, we start by iterating over each file in the postFiles 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.
Notice how we are using static utility methods from PostUtil and AuthorUtil classes for operations concerning posts and authors respectively.
PostUtil and AuthorUtil classes
In the PostUtil class, we define methods: getHtmlContentFromMdLines and getSynopsisFromHtmlContent.
The getHtmlContentFromMdLines returns a String of HTML content rendered using the List of Markdown lines passed as argument.
The getSynopsisFromHtmlContent 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 String is returned.
In the AuthorUtil class, we define method: bootstrapAuthor. 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.
Get it running
Now, when we run our Spring Boot application, or whenever we cause the Spring Context to refresh, a ContextRefreshedEvent is fired. This looks for a new blog post in the resources/posts/ directory and persists it.
To test this, let us create a posts directory inside the resources directory.
Inside it, I will create a new file, 1_Hello_World!.md, with the following content.
# Hello World! This is my *first* blog post. <br> Be sure to read future parts of this blog post series, <br> titled **Build a Markdown-based Blog with Spring Boot**.
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.
We have now created an application that persists new blog posts to our database. Now, we will work towards displaying blog posts using the Thymeleaf template engine. But that's for the next post.