Simple webdev in Java 8

I want to talk about how I came up with the idea of ​​doing web development in Java.

So, as soon as I matured for this matter, I made a short list to which the development tools I have chosen should correspond:
  • Build with maven;
  • A simple deployment and launch process;
  • Libraries should be lightweight;
  • The ability to use a template engine.

Googling heavily, I found what I need: Spark , jade4j and OrmLite .

So, the tools are selected, we will consider each of them separately.

Spark framework


I chose the latest version, which requires Java 8 to work. The framework itself is positioned as Sinatra-inspired, since I am not too familiar with Sinatra, it rather reminded me of Express for Node.js.

The idea was close, especially since there was already a positive experience with Express. The documentation seems to be poor, but during the development process you are convinced that it is more than enough. I also consider it a great plus that development does not require preliminary configuration of the environment, installation of a servlet container, and so on. All that is needed is to add a dependency to pom.xml, write a couple of lines - and everything will take off.

Jade4j


If you worked with Node.js template engines, in particular with jade, then you will probably want to use it again. Easy-to-read code, good documentation, a lot of beautiful features and experience of using earlier made me choose this template engine.

A small example of layout.jade:

doctype html
html
    head
        block headsection
    body
        header
        div#main
            block content
        footer

We implement {block blockname} elements in other files this way:

extends layout
block headsection
    title Привет, Хабр!
block content
    h1 Hello, habr!


Ormite


Most likely, some android developers are familiar with this ORM. It is easy to use, lightweight and configurable with annotations.

Here is a simple pojo example of the User class:

package com.vagga.pojo;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
@DatabaseTable(tableName = "users")
public class User {
    @DatabaseField(columnName = "user_id", generatedId = true)
    private int userId;
    @DatabaseField(columnName = "username", unique = true, canBeNull = false)
    private String userName;
    @DatabaseField(columnName = "user_pass")
    private String password;
    public User() {
    }
    /*
     * Далее геттеры-сеттеры
     */
}

Interaction with the database can be carried out through the built-in ORM Dao. I wrote a small singleton class to simplify my life (maybe the code below will upset you a bit, so I’ll make a reservation again: the main goal was the ease of implementation and use):

package com.vagga.utils;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.jdbc.JdbcConnectionSource;
import com.vagga.pojo.Category;
import com.vagga.pojo.Comment;
import com.vagga.pojo.Post;
import com.vagga.pojo.User;
import java.sql.SQLException;
public class DbUtil {
    private static DbUtil ourInstance = new DbUtil();
    private JdbcConnectionSource dbSource;
    private Dao userDao;
    private Dao postDao;
    private Dao categoryDao;
    private Dao commentDao;
    public static DbUtil getInstance() {
        return ourInstance;
    }
    private DbUtil() {
        try {
            dbSource = new JdbcConnectionSource("jdbc:mysql://localhost:3306/vagga?user=root&characterEncoding=utf8");
            userDao = DaoManager.createDao(dbSource, User.class);
            postDao = DaoManager.createDao(dbSource, Post.class);
            categoryDao = DaoManager.createDao(dbSource, Category.class);
            commentDao = DaoManager.createDao(dbSource, Comment.class);
        } catch (SQLException e) {
            e.printStackTrace();
            System.out.println("Cannot establish DB connection " + e.getMessage() + " " + e.getCause());
        }
    }
    public Dao getUserDao() {
        return userDao;
    }
    public Dao getPostDao() {
        return postDao;
    }
    public Dao getCategoryDao() {
        return categoryDao;
    }
    public Dao getCommentDao() {
        return commentDao;
    }
}

Some solutions that may depress you


Having started the coding process, I came across several problems that had to be solved in some way.

The number one problem was that Spark out of the box did not support jade, so I had to google it and figure out how to make them friends. The solution to the problem turned out to be quite simple. You just need to inherit from the TemplateEngine class and implement the interface render method.

In the same google group I found this quite working code:

package com.vagga.utils;
import de.neuland.jade4j.JadeConfiguration;
import de.neuland.jade4j.exceptions.JadeException;
import de.neuland.jade4j.model.JadeModel;
import de.neuland.jade4j.template.FileTemplateLoader;
import de.neuland.jade4j.template.JadeTemplate;
import de.neuland.jade4j.template.TemplateLoader;
import spark.ModelAndView;
import spark.TemplateEngine;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;
public class JadeEngine extends TemplateEngine {
    private JadeConfiguration configuration;
    private String directory = new File(".").getCanonicalPath();
    public JadeEngine() throws IOException {
        this.configuration = new JadeConfiguration();
        this.directory = this.directory + "/src/main/resources/templates/";
        TemplateLoader loader = new FileTemplateLoader(directory, "UTF-8");
        configuration.setTemplateLoader(loader);
    }
    @SuppressWarnings("unchecked")
    @Override
    public String render(ModelAndView modelAndView) {
        StringWriter stringWriter = new StringWriter();
        try {
            JadeTemplate template = this.configuration.getTemplate(modelAndView.getViewName());
            JadeModel jadeModel = new JadeModel((Map) modelAndView.getModel());
            template.process(jadeModel, stringWriter);
        } catch (JadeException | IOException e) {
            e.getCause();
        }
        return stringWriter.toString();
    }
}

The next problem was due to the fact that I did not know how to properly break the mapping of requests into different classes. As a result, I decided to create an interface with three methods:

package com.vagga.routes;
public interface BaseRoute {
    public void initBeforeAction();
    public void initActions();
    public void initAfterActions();
}

Next, I created the base class of the route, in which I initialized the template rendering helpers:

package com.vagga.routes;
import com.vagga.utils.JadeEngine;
import com.vagga.utils.JsonTransformer;
import java.io.IOException;
public class Route {
    protected JadeEngine templateEngine;
    protected JsonTransformer jsonTransformer;
    protected Route() throws IOException {
        templateEngine = new JadeEngine();
        jsonTransformer = new JsonTransformer();
    }
}

The implementation of the final mapper class is here:

package com.vagga.routes;
import com.vagga.pojo.Category;
import com.vagga.pojo.User;
import com.vagga.utils.DbUtil;
import spark.ModelAndView;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static spark.Spark.*;
public class HomeRoute extends Route implements BaseRoute {
    public HomeRoute() throws IOException {
        super();
    }
    @Override
    public void initBeforeAction() {
    }
    @Override
    public void initActions() {
        get("/", (req, res) -> {
            Map model = new HashMap();
            try {
                List categories = DbUtil.getInstance().getCategoryDao().queryForAll();
                User user = DbUtil.getInstance().getUserDao().queryForId(1);
                model.put("user", user);
                model.put("categories", categories);
                model.put("title", "Главная");
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return new ModelAndView(model, "home/index.jade");
        }, this.templateEngine);
    }
    @Override
    public void initAfterActions() {
    }
}

Now all the mapper classes need to be instantiated somehow. I do it right in the main class, because I haven’t come up with anything smarter (I will be grateful if someone suggests how this can be done better):

public class VaggaMain {
    public static void main(String[] args) {
        try {
            ArrayList routes = new ArrayList<>();
            routes.add(new HomeRoute());
            routes.add(new AdminRoute());
            routes.add(new ApiRoute());
            routes.forEach((route) -> {
                route.initBeforeAction();
                route.initActions();
                route.initAfterActions();
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


Total


All the goals that I set for myself were realized. I was pleased with the tools. The main problems encountered are the extreme scarcity of information on the Internet about the above tools. In general, the framework is cool, simple and makes you rejoice.

Thank you all for your attention! Constructive criticism is welcome.

Also popular now: