1. Overview
There are many web frameworks like Spring, Play, and Grails available in the Java ecosystem. However, none of them can claim to be completely immutable and object-oriented.
In this tutorial, we'll explore the Takes framework and create a simple web application using its common features like routing, request/response handling, and unit testing.
2. Takes
Takes is an immutable Java 8 web framework that neither uses null nor public static methods.
Also, the framework doesn't support mutable classes, casting, or reflection. Hence, it is a true object-oriented framework.
Takes don't require configuration files for the setup. Besides that, it provides built-in features like JSON/XML response and templating.
3. Setup
First, we'll add the latest takes Maven dependency to the pom.xml:
<dependency> <groupId>org.takes</groupId> <artifactId>takes</artifactId> <version>1.19</version> </dependency>
Then, let's create the TakesHelloWorld class that implements the Take interface:
public class TakesHelloWorld implements Take { @Override public Response act(Request req) { return new RsText("Hello, world!"); } }
The Take interface provides the fundamental feature of the framework. Each Take serves as a request handler, returning the response through the act method.
Here, we've used the RsText class to render the plain text Hello, world! as a response, when a request is made to the TakesHelloWorld take.
Next, we'll create the TakesApp class to start the web application:
public class TakesApp { public static void main(String... args) { new FtBasic(new TakesHelloWorld()).start(Exit.NEVER); } }
Here, we've used the FtBasic class that provides the basic implementation of the Front interface to start the webserver and forward the request to the TakesHelloWorld take.
Takes implements its own stateless webserver by using the ServerSocket class. By default, it starts the server on port 80. However, we can define the port in the code:
new FtBasic(new TakesHelloWorld(), 6060).start(Exit.NEVER);
Or, we can pass the port number using the command-line parameter –port.
Then, let's compile the classes using the Maven command:
mvn clean package
Now, we're ready to run the TakesApp class as a simple Java application in an IDE.
4. Run
We can also run our TakesApp class as a separate web server application.
4.1. Java command line
First, let's compile our classes:
javac -cp "takes.jar:." com.baeldung.takes.*
Then, we can run the application using the Java command line:
java -cp "takes.jar:." com.baeldung.takes.TakesApp --port=6060
4.2. Maven
Or, we can use the exec-maven-plugin plugin to run it through Maven:
<profiles> <profile> <id>reload</id> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.6.0</version> <executions> <execution> <id>start-server</id> <phase>pre-integration-test</phase> <goals> <goal>java</goal> </goals> </execution> </executions> <configuration> <mainClass>com.baeldung.takes.TakesApp</mainClass> <cleanupDaemonThreads>false</cleanupDaemonThreads> <arguments> <argument>--port=${port}</argument> </arguments> </configuration> </plugin> </plugins> </build> </profile> </profiles>
Now, we can run our app using the Maven command:
mvn clean integration-test -Preload -Dport=6060
5. Routing
The framework provides the TkFork class to route the requests to different takes.
For instance, let's add a few routes to our application:
public static void main(String... args) { new FtBasic( new TkFork( new FkRegex("/", new TakesHelloWorld()), new FkRegex("/contact", new TakesContact()) ), 6060 ).start(Exit.NEVER); }
Here, we've used the FkRegex class to match the request path.
6. Request Handling
The framework provides a few decorator classes in the org.takes.rq package to handle the HTTP request.
For example, we can use the RqMethod interface to extract the HTTP method:
public class TakesHelloWorld implements Take { @Override public Response act(Request req) throws IOException { String requestMethod = new RqMethod.Base(req).method(); return new RsText("Hello, world!"); } }
Similarly, the RqHeaders interface is available to fetch the request headers:
Iterable<String> requestHeaders = new RqHeaders.Base(req).head();
We can use the RqPrint class to get the body of the request:
String body = new RqPrint(req).printBody();
Likewise, we can use the RqFormSmart class to access the form parameter:
String username = new RqFormSmart(req).single("username");
7. Response Handling
Takes also provides many useful decorators to handle the HTTP response in the org.takes.rs package.
The response decorator implements the head and body methods of the Response interface.
For instance, the RsWithStatus class renders the response with status code:
Response resp = new RsWithStatus(200);
The output of the response can be verified using the head method:
assertEquals("[HTTP/1.1 200 OK], ", resp.head().toString());
Similarly, the RsWithType class renders the response with content-type:
Response resp = new RsWithType(new RsEmpty(), "text/html");
Here, the RsEmpty class renders the empty response.
Likewise, we can use the RsWithBody class to render the response with the body.
So, let's create the TakesContact class and use the discussed decorators to render the response:
public class TakesContact implements Take { @Override public Response act(Request req) throws IOException { return new RsWithStatus( new RsWithType( new RsWithBody("Contact us at https://www.baeldung.com"), "text/html"), 200); } }
Similarly, we can use the RsJson class to render the JSON response:
@Override public Response act(Request req) { JsonStructure json = Json.createObjectBuilder() .add("id", rs.getInt("id")) .add("user", rs.getString("user")) .build(); return new RsJson(json); }
8. Exception Handling
The framework contains the Fallback interface to handle exceptional conditions. It also provides a few implementations to handle the fallback scenarios.
For instance, let's use the TkFallback class to handle the HTTP 404 and shows a message to the user:
public static void main(String... args) throws IOException, SQLException { new FtBasic( new TkFallback( new TkFork( new FkRegex("/", new TakesHelloWorld()), // ... ), new FbStatus(404, new RsText("Page Not Found"))), 6060 ).start(Exit.NEVER); }
Here, we've used the FbStatus class to handle the fallback on the defined status code.
Similarly, we can use the FbChain class to define a combination of fallbacks:
new TkFallback( new TkFork( // ... ), new FbChain( new FbStatus(404, new RsText("Page Not Found")), new FbStatus(405, new RsText("Method Not Allowed")) ) ), 6060 ).start(Exit.NEVER);
Also, we can implement the Fallback interface to handle the exceptions:
new FbChain( new FbStatus(404, new RsText("Page Not Found")), new FbStatus(405, new RsText("Method Not Allowed")), new Fallback() { @Override public Opt<Response> route(RqFallback req) { return new Opt.Single<Response>(new RsText(req.throwable().getMessage())); } } )
9. Templates
Let's integrate Apache Velocity with our Takes web app to provide some templating functionality.
First, we'll add the velocity-engine-core Maven dependency:
<dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.2</version> </dependency>
Then, we'll use the RsVelocity class to define the template string and the binding parameters in the act method:
public class TakesIndex implements Take { @Override public Response act(Request req) throws IOException { return new RsHtml( new RsVelocity("${username}", new RsVelocity.Pair("username", "Baeldung"))); ); } }
Here, we've used the RsHtml class to render the HTML response.
Also, we can use a velocity template with the RsVelocity class:
new RsVelocity(this.getClass().getResource("/templates/index.vm"), new RsVelocity.Pair("username", username)) );
10. Unit Testing
The framework supports unit testing of any Take by providing the RqFake class that creates a fake request:
For example, let's write a unit test for our TakesContact class using JUnit:
String resp = new RsPrint(new TakesContact().act(new RqFake())).printBody(); assertEquals("Contact us at https://www.baeldung.com", resp);
11. Integration Testing
We can test the entire application using JUnit and any HTTP client.
The framework provides the FtRemote class that starts the server on the random port and provides remote control to the execution of the Take.
For instance, let's write an integration test and verify the response of the TakesContact class:
new FtRemote(new TakesContact()).exec( new FtRemote.Script() { @Override public void exec(URI home) throws IOException { HttpClient client = HttpClientBuilder.create().build(); HttpResponse response = client.execute(new HttpGet(home)); int statusCode = response.getStatusLine().getStatusCode(); HttpEntity entity = response.getEntity(); String result = EntityUtils.toString(entity); assertEquals(200, statusCode); assertEquals("Contact us at https://www.baeldung.com", result); } });
Here, we've used Apache HttpClient to make the requests to the server and verify the response.
12. Conclusion
In this tutorial, we've explored the Takes framework by creating a simple web application.
First, we've seen a quick way to set up the framework in our Maven project and run our application.
Then, we examined a few common features like routing, request/response handling, and unit testing.
Lastly, we explored the support of unit and integration testing provided by the framework.
As usual, all the code implementations are available over on GitHub.