Stichword-Archiv: java

Quick and Dirty Java Service Template

Dezember 25, 2017 1:04 pm Veröffentlicht von

To get a java service running, you don’t always need to start a tomcat and deploy a war file, as described in an earlier blog post. Even if tomcat gives you a number of benefits, such as user management and database connection handling, sometimes you do not need this and just want to start up a small http server to provide a bit of functionality.

The template contents

To get such projects started as fast as possible, I created the quick and dirty java service template, where I collected stuff I find most useful to implement a new java service:

It uses the grizzly http server to provide the web server, which makes starting an http server running java code as easy as executing a jar file.
Often, communication between a web page and a java backend works easiest by sending a JSON document. This is why we include the gson JSON parser in the template.
In addition to some java code, we will also package static content, such as html files or images to the jar package. All this can be accessed via the same URL after starting the jar file.

Checkout the service template at https://github.com/NautiluX/JavaServiceTemplate.

Start up

To build and run the server, we use mvn. So compilation and start can be done using the following two maven commands:

mvn clean install

mvn exec:java

now we can access our small server at localhost:5678.

Customize

Now for some more details to show you where to put server code and static content.
The project is set up using maven. That means all our java code can be found in src/main/java. When we start the server, by executing mvn exec:java, the class org.ntlx.server.Main will be executed, which creates the http server.

package org.ntlx.server;

import com.sun.jersey.api.container.grizzly2.GrizzlyServerFactory;
import com.sun.jersey.api.core.*;

import java.io.IOException;

import org.glassfish.grizzly.http.server.*;

public class Main {

  public static void main(String args[])
      throws IllegalArgumentException, NullPointerException, IOException {
    Main main = new Main();
    main.startServer();
  }

  public void startServer() throws IllegalArgumentException, NullPointerException, IOException {
    ResourceConfig rc = new PackagesResourceConfig(“org.ntlx.service”);
    HttpServer myServer = GrizzlyServerFactory.createHttpServer(“http://0.0.0.0:9876/calc/”, rc);
    HttpHandler httpHandler = new CLStaticHttpHandler(HttpServer.class.getClassLoader(), “/ui/”);
    myServer.getServerConfiguration().addHttpHandler(httpHandler, “/”);
    try {
      myServer.start();
      Thread.currentThread().join();
    } catch (Exception e) {
      System.err.println(e);
    } finally {
      myServer.stop();
    }
  }

}

The http server is listening on port 9876 and is searching for service endpoints in the package org.ntlx.service.

GET endpoint

To implement the code for our service, we take a look at the example implementation of the service template. We want our GET endpoint as heartbeat to check if the backend is up and running. Therefore we want it to just return
{
    “getCall”: “succeeded”
}
The code for this simple get endpoint can be found in the class org.ntlx.service.SumService in the function dummyGet:

package org.ntlx.service;

import com.google.gson.*;
import javax.ws.rs.*;

@Path(“sum”)
public class SumService {

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public String dummyGet() {
    JsonObject result = new JsonObject();
    result.addProperty(“getCall”, “succeeded”);
    return result.toString();
  }

  …
}
We tell the http service, that we want requests that come in to the path /sum to be handled by this class. With the annotation @GET we tell it, that this function shall be called by GET http requests. With the @Produces annotation we get the http response header application/json. To create this simple json string, we use the Gson library.

POST/PUT/…

To create endpoints for other type of requests, we can do the same as for get. Lets have a look at the backend for our summing service, which should sum a list of numbers coming in as JSON.

  @POST
  @Produces(MediaType.APPLICATION_JSON)
  public String dummyPost(String body) {
    CalculatorInput input = new Gson().fromJson(body, CalculatorInput.class);
    JsonObject result = new JsonObject();
    result.addProperty(“result”, input.calculateSum());
    return result.toString();
  }

We tell the server we are listening for POST calls (the same could be done for POST, PUT, DELETE, etc.). We add a String parameter to the function, which will be filled up with the request body of the incoming POST call. To get the input, we again use GSON to instantiate an object of the CalculatorInput class, which executes our – very complex – workload in the function call calculateSum().

Static Content

Static content – like html files – must be located in the class path, so the http server can find it. That means we have to rebuild the jar file any time we want to change our static content. We told the http server where to search for it (/ui/) and where to place it (/). That means we can put our static content into the folder src/main/resources/ui, and they will be included in our jar file automatically and found by the http server under the path /. That’s why we are routed to the file created as src/main/resources/ui/index.html when we visit localhost:9876 after starting the server.

Putting tomcat into a container

Februar 9, 2017 10:37 pm Veröffentlicht von

As soon as you get in touch with software there is no chance to get around one topic these days: Microservices. And wherever you hear this term there is always a second one around the corner: Docker. I myself have only little experience in productively using docker, and this is mostly restricted to the convenience of packaging stuff and installing it somewhere else via a central repository. But already this is quite cool, so why not try to go further and create a small Java service in a docker container if setting up things is so easy with docker. The goal of this post is to document how I achieved a first response of the service – setting up a Java microservice with docker should be almost no work, right? As I haven’t had much contact with tomcat and maven, I learned all this (not much in the end but it took some time) the hard way – fighting all the way through old forum posts, maven repositories, eclipse UIs, tomcat books and documentations. Please let me know if you see any potential improvements.

Setting things up

Here we go. First of all I created a docker-compose file that shows how my microservice-powered project will look like:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
proxy:
image: nginx
links:
- frontend
- backend
volumes:
- ./proxy/nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- "80:80"
frontend:
image: nginx
volumes:
- ./frontend:/usr/share/nginx/html
backend:
image: tomcat
links:
- database
volumes:
- ./backend/tomcat-users.xml:/usr/local/tomcat/conf/tomcat-users.xml
database:
image: mysql
environment:
- MYSQL_ROOT_PASSWORD=topsecret
- MYSQL_DATABASE=mydb
- MYSQL_USER=mydbuser
- MYSQL_PASSWORD=

I know container-links are not the most modern way to go in the docker universe, but for the ease of use and to show how things are tangled together, I still use them for this small project. As you can see, we have four different containers. One for the frontend, containing the great UI of our application. A second one containing the database with all the data the application will need to run. Don’t worry, I won’t bother you with boring database setup in this blog post. It’s only about the third thing: the tomcat container. For this container we add a link to the database, of course, and we mount a local file to override the user configuration of the tomcat.
This is needed to be able to show the manager application and to deploy remotely to the tomcat server. Here is the config file as I created it. (I copied it out of the container and adjusted it, maybe you should do the same to make sure it’s compatible with your version of tomcat. And, for productive usage, of course you should use a password for the admin user. But configuring maven is more complicated then, so for now it’s ok without for me.)

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version='1.0' encoding='utf-8'?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<tomcat-users xmlns="http://tomcat.apache.org/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
version="1.0">
<!--
NOTE: By default, no user is included in the "manager-gui" role required
to operate the "/manager/html" web application. If you wish to use this app,
you must define such a user - the username and password are arbitrary. It is
strongly recommended that you do NOT use one of the users in the commented out
section below since they are intended for use with the examples web
application.
-->
<!--
NOTE: The sample user and role entries below are intended for use with the
examples web application. They are wrapped in a comment and thus are ignored
when reading this file. If you wish to configure these users for use with the
examples web application, do not forget to remove the <!.. ..> that surrounds
them. You will also need to set the passwords to something appropriate.
-->
<!--
<role rolename="tomcat"/>
<role rolename="role1"/>
<user username="tomcat" password="<must-be-changed>" roles="tomcat"/>
<user username="both" password="<must-be-changed>" roles="tomcat,role1"/>
<user username="role1" password="<must-be-changed>" roles="role1"/>
-->
<role rolename="manager-script"/>
<user username="admin" password="" roles="manager-script"/>
<role rolename="manager-gui"/>
<user username="tomcat" password="topsecret" roles="manager-gui"/>
</tomcat-users>

The last container is used to reverse-proxy the requests of the application either to the frontend or to the backend, depending on the URL. In this example, I configured nginx to lead all the requests starting with /backend (http://localhost/backend) or /manager to the backend container (make sure to set it to the correct port of tomcat, 8080) and all others to the frontend container. So we need to make sure the servlet we are going to create is available under /backend, so they can be routed through the reverse proxy correctly.
We run docker-compose up -d as usual and some seconds later the containers are up and running. If you now go to http://localhost/backend you should already see tomcat greeting you with a 404 error page. Installing tomcat has never been so easy.

Deploying to tomcat

Next, we want to be able to deploy a servlet to the tomcat running in the docker container. To do this, I decided to use maven to build and deploy the project. To get the initial setup, I ran maven with the following command line:
mvn archetype:generate -DgroupId=com.ntlx -DartifactId=backend -DarchetypeArtifactId=maven-archetype-webapp
After the execution of this command you will find your self in a folder structure, containing all the files and folders you always wondered where they come from, and who knows where to put them:

./pom.xml
./src
./src/main
./src/main/resources
./src/main/webapp
./src/main/webapp/index.jsp
./src/main/webapp/WEB-INF
./src/main/webapp/WEB-INF/web.xml

The pom.xml now needs some further adjustments because we want to deploy to a remote tomcat server. Here is the complete file, as it looks for me:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ntlx</groupId>
<artifactId>backend</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>backend Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>


</dependencies>
<build>
<finalName>backend</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<failOnMissingWebXml>true</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<url>http://localhost/manager/text</url>
<server>TomcatServer</server>
<path>/backend</path>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<silent>true</silent>
<artifactItems>
<artifactItem>
<groupId>javax</groupId>
<artifactId>javaee-endorsed-api</artifactId>
<version>6.0</version>
<type>jar</type>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

It already contains the plugins needed to deploy a servlet and it contains the information to deploy onto a remote tomcat. In our case the remote tomcat is localhost, port 80 (remember: reverse-proxied to port 8080 in the tomcat container). /manager/text is needed to deploy our service. That’s why we also created a reverse-proxy for this URL.

That’s nearly it. If we now run
mvn tomcat7:deploy
it should be able to deploy our web application to the tomcat server. Go to http://localhost/backend again to see the hello world page, maven created earlier.

Adding a servlet

To provide our RESTful service in the end, we want to use a servlet. Therefore, we need to create a .java file for our servlet class at the right place: src/main/java. Create this folder, add some subfolders for your package like com/ntlx/ and add a Java Servlet file there. I myself copied the example from the eclipse wizard for servlet creation:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
package com.ntlx;

/**
* Servlet implementation class FirstTest
*/
@WebServlet("/FirstTest")
public class FirstTest extends HttpServlet {
private static final long serialVersionUID = 1L;

/**
* Default constructor.
*/
public FirstTest() {
// TODO Auto-generated constructor stub
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.getWriter().append("Served at: ").append(request.getContextPath());
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}

}

As you can see, we set the URL to /FirstTest. This is the relative address where the servlet will be found. So, after a run of 
mvn tomcat7:redeploy

you should see the response of the servlet at http://localhost/backend/FirstTest

That’s it, now we can start developing the service! I think this is quite handy as you can deploy the same tomcat on a different machine, like the productive one instead of your developer notebook, and it will work as before. Of course we have to think of a process to package the servlet with the docker container, but this shouldn’t be too much effort.