Jodd µicro frameworks

We want to inspire the world to create. To use tech skills and build something truly... Awesome. Beautiful. Helpful. Impactful. Now you can produce lightweight code and focus on unleashing your full potential. Jodd is a set of developer-friendly and open-source Java micro-frameworks. It's designed to make things simple, but not simpler.

This tutorial is a good place to get an overview of how Jodd works. That's what we have here: a step-by-step example of how to build a web application using Jodd only. In just 30 minutes, you will get from "zero to hero".

The tutorial explains how to make a simple web app from scratch: an app that shows messages and message comments stored in the database, just like a simple blog.

Let's go!

http://jodd.org

1

Prepare Yourself

The first step is not about Jodd at all. Instead, we will lay down the infrastructure: project folders, Gradle, Tomcat, and MySql.

Please check out this tutorial - we have prepared all the files you need!

Folders

In the project's root, create a standard Maven folder layout for web projects.

                  
/root
    /src
      /main
          /java
          /resources
          /webapp
      /test
                  

Gradle

We will use Gradle for building; make sure you have it installed. Create a simple build.gradle script and add a single dependency to Jodd Joy. That's all we need for now :)

                  
apply plugin: 'war'
version = '1.0'
repositories {
    mavenCentral()
}
dependencies {
    compile 'org.jodd:jodd-joy:4.1.1'
}
          

Execute the following command in the project's root: gradle wrapper to initialize your Gradle project. This will create the shell script gradlew in your root which you can use from now on to run the build process.

IDE

Once you set the Gradle, open the project in your IDE. Create a web artifact that can be deployed. IntelliJ IDEA works nicely with this setup.

Database

It's time to set up the database: jodd-tutorial in the MySql. There are just two tables: jd_message and jd_response: one message may contain several comments. Yes, we've made a bad decision by naming the 'comments' 'responses'; please forgive us.

Thanx to Docker, everything is set for you:

            
> cd docker
> docker-compose up -d
          

Tomcat

Don't forget the Tomcat! Download and unzip latest Tomcat bundle. You may delete the default web application. Register a new Tomcat installation in your IDE.

View snippets »

2

Build The Core

First thing's first. We need to build the simplest web app possible that works. Let's start!

There are only 3 things we have to do for now:

  • Add Gradle dependecies,
  • Register the web application,
  • Set the database connection properties.

Again, the source for this step awaits on GitHub!

Gradle dependencies

Obviously, we'll need to add some more dependencies: for the MySql driver and servlets jars.

                
dependencies {
    compile 'org.jodd:jodd-joy:4.1.1'
    runtime 'mysql:mysql-connector-java:5.1.45'

    providedRuntime 'javax.servlet:javax.servlet-api:4.1.0'
    providedRuntime 'javax.servlet.jsp:javax.servlet.jsp-api:2.3.1'
}
          

Web application

Though we can register the app in web.xml, it's easier to simply create a WebListener based on the JoyContextListener class. This is all we need for now: just an empty subclass annotated with @WebListener annotation.

                
@WebListener
  public class AppContextListener extends JoyContextListener {

  }
          

Properties

By default, Jodd Joy properties are stored in the joy.props file, located in the classpath root. The props file is a powerful extension with Java properties. The initial properties just set the database connection and the connection pool.

                
# database
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/jodd-tutorial?useSSL=false
jdbc.username=root
jdbc.password=root!

# db pool
dbpool.driver=${jdbc.driverClassName}
dbpool.url=${jdbc.url}
dbpool.user=${jdbc.username}
dbpool.password=${jdbc.password}
dbpool.maxConnections=50
dbpool.minConnections=5
dbpool.waitIfBusy=true
      

Make sure that your IDE recognizes *.props files as resources.

Run!

And that's all! These three simple steps are all you need to run the web application :)

This is how the project looks like:

Layout

Yes, you can start Tomcat now and check if everything works. Just watch Tomcat's log - the app should be up in a couple of seconds!

View snippets »

3

Show Me Some Messages

We can start coding our application! Let's display the last 10 messages on the index page.

This step requires some more coding. Download it all instead! Or go ahead and fire up that keyboard!

Model

We need two model classes: Message and Response (for comments). These will be mapped to the database tables. As they are simple POJOs, we just need to mark them with appropriate DbOom database mapping annotations.

Message class:

                
@DbTable
public class Message {

    @DbId
    private long messageId;

    @DbColumn
    private String text;

    // ...getters and setters...
}
      

... and comments class:

                
@DbTable
public class Response {

    @DbId
    private long responseId;

    @DbColumn
    private String text;

    @DbColumn
    private long messageId;

    // ...getters and setters...
}
      

Once again, simple POJOs with Jodd annotations.

Please note that we haven't said anything about the relationship between these two entities. We will discuss this in more detail later; for now, just keep in mind that Jodd is all about mapping, so relations are not defined up front. Instead, you'll define them with your queries when needed.

Database configuration

It's a good practice to use a prefix for the database tables that your application uses. We can specify the prefix in Jodd annotations, but it's better to specify a default prefix for all entity beans. You can do this in joy.props. While you're at it, we can also enable the debug mode for more verbose logs.

                
db.debug=true
db.dbOomConfig.tableNames.prefix=jd_
        
      

Java Service

Finally, it's time to read the database and return the data! We are going to write a bean that contains the (business) logic. We will mark this bean as @PetiteBean to have it registered in the application's Petite container. No other registration is required!

Since our method accesses the database, it has to run under a database transaction. To enable a database session for this method, we just need to mark it with a transactional annotation.

Instead of SQL, we will write queries using T-SQL (Template SQL). It's an extension of the common SQL that provides special macros which may reference to our Java entities. This significantly simplifies writing native SQL queries!

                
@PetiteBean
public class AppService {

    @ReadOnlyTransaction
    public List<Message> findLastMessages(int count) {
        DbSqlBuilder dbsql =
            sql("select $C{m.*} from $T{Message m} " +
            "order by $m.messageId desc limit :count");
        DbOomQuery dbquery = query(dbsql);
        dbquery.setInteger("count", count);

        return dbquery.list(Message.class);
    }
}
      

Our service is simple POJO class, annotated with @PetiteBean annotation. You need nothing else, no XML files, registration, etc. And you may use plain SQL queries instead if you wish so.

Noticed the @ReadOnlyTransaction annotation? Yeah, that's all that's required to supply your database code with a transactional database session.

Awesome, right?!

View

For the web layer, we'll need a Madvoc action and JSP that renders the result. Madvoc action is a simple bean, annotated with @MadvocAction. Created on every request, Madvoc action recognizes the container's context and you can easily inject your services.

                
@MadvocAction
public class IndexAction {

    @PetiteInject
    AppService appService;

    @Out
    List<Message> messages;

    @Action
    public void view() {
        messages = appService.findLastMessages(10);
    }
}
      

Madvoc actions are automatically registered by default. You can use manual registration instead!

Madvoc action uses simple conventions and smart annotation to define input and outputs and target JSP. Our example has only one output – a list of messages.

                
<%@ taglib prefix="j" uri="/jodd" %>
<html>
<body>
<h1>Messages</h1>

<ul>
<j:iter items="${messages}" var="msg">
    <li>${msg.messageId} ${msg.text}
</j:iter>
</ul>
</body>
</html>
      

Another gem for you: there is an entire powerful tag library you can use in your JSPs!

Run!

Start Tomcat and go to http://localhost:8080/index.html. You will see the last 10 messages!

View snippets »

4

Messages and Comments

It's time to display messages-related comments.

Only few changes ahead! Still, you may checkout the updates.

Model

Add a list of Response objects to the Message class as a simple property. It will hold comments, but only when we want so. It's important to remember this: related comments will not be automatically pre- or lazy-fetched. Instead, we just map them and use them when we want to. Alternatively, you can use different beans, depending on the relationship you need.

                
@DbTable
public class Message {

    ...

    private List<Response> responses;

    public List<Response> getResponses() {
        return responses;
    }

}
      

Note that responses is a read-only property. There is no special rule about this, it's just how we want to have it in this tutorial.

Service

To fetch all messages and their (optional) comments, we need a left join between these two tables. If we simply change the query and add additional mapping, then each row of the result set will be mapped to Object[2] – it will hold the message and its single comment. We don't want that! Instead, we want to have a single message filled up with all its comments (stored in the new property). Here's an easy way to do it.

First, we inject comments into messages. Using DbOom hints lets us specify T-SQL so that one result object is injected or added into the property of another resulting object. In our case, we can define that Response object is added into the new list property of Message class.

Secondly, we prevent the repetition of messages with the same id. When a message has more than one comment, the same message data will be repeated in the joined results for each comment. This would give us many Message instances of the same entity (i.e. with the same ID). To fix this, we just need to enable entityAware mode that caches entities during the lifetime of a query.

                
@Transaction
public List<Message> findLastMessagesWithResponses(int count) {
    DbSqlBuilder dbsql = sql(
            "select $C{m.*}, $C{m.responses:r.*} " +
            "from $T{Message m} " +
            "left join $T{Response r} using ($.messageId) " +
            "order by $m.messageId desc limit :count");

    DbOomQuery dbquery = query(dbsql);
    dbquery.entityAwareMode(true);
    dbquery.setInteger("count", count);

    return dbquery.list(Message.class, Response.class);
}
      

When entityAware mode is enabled, two things will happen. First, all entities during the lifetime of a query will be cached, so there will be no two instances of the same entity.Second, the result set rows will be compacted if needed. For example, if one message has two comments, that would give two rows in the result set. Compaction gives us one message with two comments in the list.

View

The change in the web layer is trivial. Our action has to call a new method and JSP has to display the message comments.

                
<ul>
<j:iter items="${messages}" var="msg">
    <li>${msg.messageId} ${msg.text}
        <ul>
            <j:iter items="${msg.responses}" var="resp">
                <li>${resp.responseId} ${resp.text}</li>
            </j:iter>
        </ul>
    </li>
</j:iter>
</ul>
      

If everything goes well, the output should look like this:

output

View snippets »

5

Add A New Message

So far, we've just been reading from the database. Let's see now how to add some new messages.

Congratulations, we are almost done! Keep track with code changes or just type them along with us!

Service

Jodd provides shortcuts for common database operation. You don't have to write queries for storing entities, searching for them by ID or property value, updating, deleting, etc. With the help of DbEntitySql you can write simple one-liner calls in Java to get the same result.

                
@ReadWriteTransaction
public void addMessage(Message message) {
    DbEntitySql
            .insert(message)
            .query()
            .autoClose()        // not mandatory, but nice
            .executeUpdate();
}
      

This time, however, we need to mark the service method with a different Jodd's transaction annotation, which signals that the transaction is not read-only.

View

Obviously, there will be more changes to the web layer. We need two more actions: one to show a page with a form, and the other to handle form submissions. We can define both actions in the same class, which is convenient. The form handler won't have any extension and will handle only POST requests.

          
@MadvocAction
public class MessageAction {

    @PetiteInject
    AppService appService;

    @Action
    public void view() {
    }

    @In
    Message message;

    @POST @Action
    public String add() {
        appService.addMessage(message);
        return "redirect:/<index>";
    }
}
      

This action defines two mappings: /message.html mapped to the #view() method and /message.add mapped to the #add() method. The first action handler just renders the form with the message.jsp page. The second action handler stores submitted message.

          
<html>
<body>
<h1>Add Message</h1>

<form action="/message.add" method="POST">
    <textarea name="message.text" rows="5" cols="50">
    </textarea>
    <button type="submit">Submit</button>
</form>

</body>
</html>
      

There is one thing to notice here - the name of the parameter: "message.text". It specifies where the submitted value will be injected into. Since the message property of the action is marked with @In, text value will be inserted into message property text, after a new instance of Message is created.

After the new message is submitted and stored in the database, we'll want to redirect to the index page. While we can just hardcode the page path, there are better ways to go about it. One way is to use aliases. Each action method has its alias name and that alias handle represents an action path. So even if you change your code and rename the handler method, Jodd will find the aliased target path.

Alternatives

There is even a shorter alternative for basic manipulation of an entity. By using GenericDao (or somewhat enhanced AppDao) you can perform basic database operation using just a single method call.

                
@PetiteInject
AppDao appDao;

@ReadWriteTransaction
public void addMessageAlt(Message message) {
    appDao.store(message);
}
      

There is another option: use Result to specify the redirection in the web action. With this helper class you can define a handle for any action method by calling it! Then you don't need to define any custom aliases for actions.

                
@MadvocAction
public class MessageAction {

    final Result result = new Result();

    @POST @Action
    public void add() {
        appService.addMessageAlt(message);
        result.redirectTo(IndexAction.class).view();
    }

}
      

Result instances don't have to be explicitly instantiated as shown above. You can use any Result subclasses with your additional helper methods.

The alternative code works the same. It's up to you to decide what to use :) That's what makes Jodd beautiful :)

Run

Run your web application again. Go to the message page and add new messages. It's fun! :)

View snippets »

6

Give Me Some REST!

Jodd is quite flexible: there are always several different ways to get to the same result. Adding a REST api is easy: instead of using @Action, we can use @RestAction! However, each annotation comes with a different configuration. The @RestAction uses a different mapping convention: action method names should now start with HTTP method names.

Let's add a new action class: MessageAction and action method get(). There is also a nice way to specify path patterns that will be matched and populated into the @In parameters. Just use string template and {} macros.

                
@MadvocAction
public class MessageAction {

    @PetiteInject
    AppService appService;

    @RestAction("{messageId}")
    public Message get(@In long messageId) {
        return appService.findMessageById(messageId);
    }
          

See the REST code changes - there are more tricks inside!

And that's it! Return a message object and it will be serialized to JSON.

What We Have Learned?

We hope this tutorial has got you interested in Jodd. You have learned the essentials of core Jodd micro-frameworks. There is a Petite dependency-injection container to glue the components together. We use Madvoc for the web layer. We communicated with the database using the DbOom mapper, enabling the JTX transaction using the Proxetta proxies. And all the application components were simple beans, marked just with annotations.

In our tutorial, we use Jodd Joy - an application template for faster web application development. It is quite configurable and combines the best Jodd practices with a pragmatic approach.

Possibly the single most important thing to learn here is that Jodd doesn't offer a single out-of-the-box solution that claims to magically solve all the problems. Instead, Jodd provides solid, but lightweight frameworks; it gives you the freedom to code your solutions the way you want.

And Beyond!

There are many other Jodd features not that we haven't covered in this tutorial, such as various micro-frameworks you might like to use in your application... You may continue by adding Decora page templates for the entire site. Then you can optimize resources using HtmlStapler. Why not add authentication and authorization interceptors? Forms may be validated using VTor. Or, you can add more proxies using Proxetta. If you need to send some emails, fire a few HTTP requests, parse some HTML as if using jQuery; or just need simple utility methods... Jodd has it all.

http://jodd.org

Built by Igor & friends with .