Jiffy
Java Web App Framework for High Concurrency. Jiffy is designed to allow you to quickly create a scalable and performant web application using Java. Its inspiration comes after working with other frameworks like Java's Play! framework, Ruby on Rails, and PHP's CodeIgniter.
Install / Use
/learn @bluedevil2k/JiffyREADME
Jiffy
Jiffy is a quick and easy Java Web App Framework for scalable applications.
The Basics
Jiffy is designed to allow you to quickly create a scalable and performant web application using Java. Its inspiration comes after working with other frameworks like Java's Play! framework, Ruby on Rails, and PHP's CodeIgniter. It's design goals are simple:
- Allow intro-level and moderate-level Java programmers to use it
- Get an app up and running as quickly as possible
- Be as easy to learn as possible from a few examples
- Convention over configuration to the extreme
- Get an application that will scale to thousands of users out of the box
To be honest, I feel there is a HUGE discrepency between current Java web application frameworks like Play!, DropWizard, and vert.X and the skill level of a majority of Java programmers (If you don't know the skill level of most Java programmers, just visit StackOverflow sometime). For all these programmers, the popular frameworks are unusable. The learning curve and level of skill required to use these frameworks correctly are beyond the intro-level and moderate-level Java programmer. While these frameworks are solving real-world problems, these problems only exist in the most complex 5% of web applications. For the other 95%, a simple and easy framework like Jiffy will give you everything you need with a much smaller learning curve.
Play! 2's complex example
public static Promise<Result> index() {
Promise<Integer> promiseOfInt = Promise.promise(
new Function0<Integer>() {
public Integer apply() {
return intensiveComputation();
}
}
);
return promiseOfInt.map(
new Function<Integer, Result>() {
public Result apply(Integer i) {
return ok("Got result: " + i);
}
}
);
}
The same code in Jiffy
public static ServiceResponse index(ServiceInput input) {
int result = intensiveComputation();
HttpResponse response = new HttpResponse();
response.nextPage = "/index.jsp";
return response;
}
(Just to be clear, I LOVE Play!, especially version 1, but it's not a solution for everyone).
Building Blocks of Jiffy
- Controllers that follow two conventions. Choose the simple /controller/method format (RPC - Remote Procedure Call) like in CodeIgniter or use a RESTful approach like in Rails. Either way, no route files are necessary.
- The ability to deploy to one server or multiple servers without changing your code by abstracting out Sessions from a single JVM.
- An extremely powerful and scalable DB class, utilizing built in connection pools, and scalable to tens of thousands of concurrent users.
- Closely tied together Model and DB layers, allowing for extremely simple DB->Model coding. No JPA or XML configuration files, just standard SQL code.
- Built in Security - protect every controller method and every JSP page with a simple Annotation.
- An abstraction of the Cache layer, utilizing a caching layer not tied to a single JVM.
- Built in web filters to cache and compress JS/CSS and images.
- Optimized for Apache Tomcat, and will run on any Java Servlet engine as a WAR file
- Works with MySQL, MariaDB and Postres databases
- Works with any memcached client (tested with Couchbase)
Database
The database layer in Jiffy is designed to make it simple to code for, but also scalable to thousands of concurrent users. It was created to be much simpler to use than JPA for people that are comfortable writing a little SQL code.
It utilizes connection pooling from Tomcat 7's DBPool class and database utility methods and abstractions from Apache's DBUtils class. The end result is a DB utility class that handles eveything you could want from your DB abstraction layer.
DB Examples
// To get one User in the database
DB.selectOne(UserData.class, "WHERE id=?", 1);
// To get all the Users in the database
DB.selectAll(UserList.class);
// To count the users in the database
DB.count(UserData.class);
// To count the users with a distinct first name
DB.countDistinct(UserData.class, "@firstName@");
// It can also properly handle transactions
try
{
DB.openTransaction();
DB.commitTransaction();
}
catch (Exception ex)
{
DB.rollbackTransaction();
}
Model
The model layer is closely tied into the Database layer for a seamless integration just by using a few Annotations.
Model Examples
@DBTable
public class User
{
@DBColumn
public String username;
@DBColumn(name="family_name")
public String lastName;
@DBHasOne
public Address address;
@DBHasMany
public PhoneNumber phoneNumbers;
}
Model & DB Integration
With the Model classes annotated properly, the DB abstraction layer can use introspection and reflection to create and map all the database fields into the Java objects. Additionally, the integration has been set up to create an ArrayList of objects when more than 1 object is returned from the SQL.
Model/DB Example
// define the class to store many User objects
public class UserList extends ArrayList<User>
// Use the DB class to populate a List of admin Users
UserList admins = DB.selectAll(UserList.class, "WHERE @role@=?, "admin");
Controllers
There are 2 styles you can use to route to your controllers, RPC-style or REST-style. You can either use the PHP style controller/action to trigger the funcion controller.action() or you can use the RESTful services like in Ruby on Rails. Either way will work, it's up to you which one you prefer.
RPC-Style
You can define actions in your controller quickly using this style. This always takes the form of "/rpc/controller/action", where the "rpc" prefix signifies how you want the domain to be parsed by the server, RPC-Style as opposed to REST-Style.
In your JSP pages, you should refer to the controllers and the actions in an "underscore" style. The controllers should then be named in a "camelCase" style. This is the common setup for all Java/PHP/Ruby frameworks. The conversion happens automatically on the server.
Example of RPC-Style Controllers
<!-- On the HTML page -->
<form method="post" action="/rpc/user_session/login">
// Will trigger this function
public class UserSessionController
{
public static ServiceResponse login(ServiceRequest input){}
}
REST-Style
The REST style implementation of the controllers has been copied directly from Rails. The 6 possible REST actions are mapped directly to 6 method names in the controller.
GET => index()
POST => create()
GET w/ ID => show()
DELETE w/ ID => destroy()
POST w/ ID => update()
PUT w/ ID => update()
Example of REST-Style Controllers
<!-- On the HTML page -->
<form method="post" action="/rest/user/1">
// Will trigger this function
public class UserController
{
public static ServiceResponse update(ServiceRequest input){}
}
Parameters
The Objects ServiceRequest and ServiceResponse contain all the request attributes you need to parse the request and the response contains all the attributes needed for a response.
String username = input.req.getParameter("username");
String password = input.req.getParameter("password");
Responses
Then, at the end of your method, you can return the proper response type, either a HttpResponse, JsonResponse, ErrorResponse, or NoResponse. The server will handle everything for you from that point.
HttpResponse response = new HttpResponse();
response.nextPage = "/index.jsp";
return response;
JsonResponse response = new JsonResponse();
response.text = new JSONObject(myObject);
return response;
Security on Controllers and JSP's
Security and Authorization is built into Jiffy. I always felt that every framework left this off the list of their features - every application will ultimately have a user login feature, so why not include it in the framework itself.
Every Controller and JSP has the ability to add Access control to it for certain users. This can be done in the Controller by simply adding an Annotation to the method with an access level for the function
// only the ADMIN can access this function - all others will get a 500 error
@Service(access=Roles.ADMIN)
public static ServiceResponse index(ServiceRequest input) throws Exception
// or in a JSP file
Security.validateAccess(request, Roles.ADMIN);
Jiffy Configuration Files
I've always hated in frameworks that they don't include a simple way to provide configuration settings, and a simple way to access them. Rails is a good example of the right way to do it, which uses global variables to provide access to configuration variables. Jiffy tries to make this even easier.
Any configuration you place into one of the properties files will be accessible anywhere in the application.
Jiffy loads up 3 configuration files, 1) jiffy.properties 2) environment.properties 3) ENVIRONMENT.properties with each property file overriding the settings already loaded. This lets you nest configurations and specify settings specific to only one deployment.
Config Example
// In jiffy.properties
dbPassword = ABCDEF123
// In the file named environment.properties
environment = development
// In the file named development.properties
dbPassword =
///////
// If the environment was set to production instead
///////
// In the file named production.properties
dbPass
