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:
- Main class
- Controller class
- Routing plan
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="" rel="stylesheet" type="text/css" /> </head> {% var session = context.getSession(); %} <body> <div id="content"> <h3> , </h3> {% block content %}{% endblock %} </div> <script src=""></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