📜 ⬆️ ⬇️

Fuga Framework - A Small Java Web Framework

In this article I would like to talk about my framework, which I immodestly called Fuga Framework




')

Lyrical introduction


When I was in my second year, I was invited to develop a web application for one laboratory of the Department of Theoretical and Experimental Physics. The application itself was pretty simple, but it had some unusual features. First of all, it was necessary to choose in what language and with the help of what technologies to start the development of this application itself. Without hesitation, I decided to do everything in PHP without using any framework. Of course, I considered all the options known to me, but the application then seemed simple and I did not want to bother much. A week later I finished the preliminary version of the application.

The result worked. But after a while I had a feeling of dissatisfaction and concern that everything could break at any moment. And for good reason. When the application was hosted on the hosting, a lot of bugs accumulated. Starting to correct these ridiculous mistakes, I realized that I had to use some framework after all.

At that moment, we started the “Programming in Java” course, where in order to get the exam, at the end of the semester, we had to submit any project made in this language. And I had an idea to make a Java microform. Since I still needed to redo a clumsy lab application, I was microfining for this whole semester. When creating this framework, I was inspired by the popular Play Framework. As a result, I got a working web application for the laboratory, which was not only written in Java, but it turned out to be much more stable and had the ability to easily expand. My "programmer" happiness knew no bounds. Inspired by the writing of frameworks, I separated it from the application itself, did refactoring, and began to expand it with all sorts of possibilities. I successfully used this framework in one training and two more personal projects, and also I received some good reviews from some experienced developers.

Start


Fuga Framework is an embedded web framework that requires only Netty and log4j in dependencies. This allows you to use the framework not only to create a classic web application, but also to embed it in other applications to easily create a web interface. At the moment, Fuga Framework includes an HTTP server, a router and a template engine.

Any application using the Fuga Framework should have at least three files:

In the main class, we configure the application and start the web server:

package com.example; import com.showvars.fugaframework.FugaApp; public class HelloWorldApp extends FugaApp { @Override public void prepare() { getRouter().loadFromResources("/routes/helloworld.routesmap"); } public static void main(String[] args) throws Exception { new HelloWorldApp().start(); } } 

Here we only configure the router using a special file. Here is his example:

 use com.example.controllers GET $/ HelloWorldController.index() 

In the first line, we made an import of the package, which further allows us not to write the full path with the name of the package to the controller classes. In the next line, we indicate to the router that any GET that satisfies the regular expression / should be sent to the index() method in the HelloWorldController class. If you come across the Play Framework, you may notice the similarity of this routing file with a similar file in the Play Framework. But in fact, in the Fuga Framework, the router can be configured much more flexibly using additional constructs, which I will discuss later.

So, in the routing plan file, we sent a request to the index() method, which is in the HelloWorldController class. Let's create it:

 package com.example.controllers; import com.showvars.fugaframework.foundation.* public class HelloWorldController extends Controller { public static Response index(Context ctx) throws Exception { return ok(" !"); } } 

Any method that calls the router must be static and be sure to take an object of type Context as the first argument. This object stores references to the current request, including cookies and sessions, as well as a link to the server instance. For convenience, we only inherit from the Controller class static methods such as ok() , proceed() , notFound() , redirect() , etc.

Done! Now you can start the application and open the browser: http: // localhost: 8080 /

Customization


All settings of the web server, the template engine and everything else is done using the set() method of the Configuration object. This object can be obtained using a getter in the prepare() method, or from a Context object in the controller.

Router


The routing plan, if necessary, can be described more difficult. At the moment, the possibility of using the following designs:

In regular expressions, you can use capture groups, and transfer their values ​​to the controller:

 GET $/(.*) com.example.ExampleController.index(1) 

You can supply constants to the controller, as well as specify the type of value. In this case, they will be listed automatically:

 GET $/(\d+) com.example.ExampleController.index(1:int, "3.14":double, "some string") 

The first two route parameters are optional and can be written in any order:

 com.example.ExampleController.index() GET com.example.ExampleController.index() GET $/hello com.example.ExampleController.index() $/hello GET com.example.ExampleController.index() 

One route can send a request to several controllers in sequence:

 GET $/(\d+) { HelloWorldController.check(1:int) HelloWorldController.index() } 

If the controller returns proceed() or null value, the router will try to execute the next controller in the list.

Nested routes:

 $/page/(\d*) { GET $/page/(\d+) HelloWorldController.get(1:int) POST $/page/(\d+) HelloWorldController.post(1:int) GET { HelloWorldController.check(0) HelloWorldController.index() } } 

You can use predefined methods to quickly return statics to the user:

 GET $/(.*) asset(1:String) GET $/ view("index.html") 

Template engine


In Fuga Framework, the template engine is based on the Mozilla Rhino JavaScript engine, which is built in by default in JRE 7 and higher.

For output, use the {# <> #} tag, where the JavaScript expression is inserted. And to insert a simple code, the {% <> %} tag is similarly used. It is also possible to extend other templates with the following constructions:

 @extend base.html {% block content %} <h2> !</h2> {% endblock %} 

To display the template to the user, you need to use the view() method from the Controller class in the Controller :

 return ok(view("index.html")); 

or directly in the routing plan write view() instead of the controller:

 GET $/ view("index.html") 

By default, the template engine compiles and caches the template once the first time it is called. But by changing the parameter fuga.templates.alwaysrecompile you can ensure that the template is compiled each time. This will allow you to quickly edit the template without restarting the server.

When writing templates, not only JavaScript is at our disposal. Since the Mozilla Rhino engine allows access to all Java classes, we get the entire standard library, as well as objects of the Context and TemplateApi classes. The first one is identical to the input to the controller, and the second is a class that has a set of useful functions, such as asset() , escape() , etc. Example:

 <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html" /> <link href="{# api.asset('css/common.css') #}" rel="stylesheet" type="text/css" /> </head> {% var session = context.getSession(); %} <body> <div id="content"> <h3> , {# session.getString('user') #}</h3> {% block content %}{% endblock %} </div> <script src="{# api.asset('js/common.js') #}"></script> {% block js %}{% endblock %} </body> </html> 

Conclusion


When creating the framework, I was not guided by any standards or well-established principles. In addition, this framework is quite raw. Therefore, I am ready to listen and take into account any comments and suggestions;)

GitHub: https://github.com/IntCode/Fuga-Framework

Source: https://habr.com/ru/post/265597/


All Articles