When Java meet Docker... Part I
Tags: java, docker, cloudnative, tutorial
Published: 2022-01-18 23:21:16

In this post, we are going to learn the very basics of how to run a java application inside a Docker container, we will start with a few definitions, and then we will build a small silly Java application which will, at the end of the post, run in a container.

This, will be a first post of a series about Java and Docker, our application will evolve along the series, until we reach the cloud :cloud: !! So let's start the journey :airplane: 

## Cloud-Native
Before we start talking about Docker and Java, let's have a few words about Cloud-Native, a term we hear often in the IT world nowadays. So what cloud-native means ? 
Here is how CNCF (Cloud Native Computing Foundation) defines it : 

> Cloud native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach.

> These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil.

In other words, Cloud-Native is a set of principles and technologies we should follow and use to build highly available, scalable, reliable and resilient software, and all of this with speed and agility.

But, isn't cloud, cloud-native ? Well, we need to differentiate between the two, as explained by Cornelia Davis in her book [Cloud Native Patterns](https://www.manning.com/books/cloud-native-patterns), when talking about differentiating the cloud-native from the cloud :
> the latter is about where, whereas the former is about how and is the really interesting part).

## Docker
But what docker have to do with all of this ? Wait, we are almost there :blush:, one of the pillar of cloud-native is the containerization, and the most popular software to manage containers and ship applications into it is **Docker** !!

If the concept of containerization is a decade old in Linux systems (cgroups and kernel namespaces), the emergence of Docker accelerated the use of this technology. A glimpse of docker underlying technology can be found in the official documentation : 

> Docker is written in the Go programming language and takes advantage of several features of the Linux kernel to deliver its functionality. Docker uses a technology called namespaces to provide the isolated workspace called the container. When you run a container, Docker creates a set of namespaces for that container. 

> These namespaces provide a layer of isolation. Each aspect of a container runs in a separate namespace and its access is limited to that namespace.

## What are we going to build
Now that we have the context, we can start working on our Java application and run it in a container later on.

First, we are going to code a very simple application that query a MySQL database and get a list of football (Soccer in North America ) players. Then we will create a jar file from this application and write a dockerfile to create a Docker image and then run container where our application will be executed.

We will also deploy MySQL database in a Docker container, run everything and see the magic.

At the end, our application should looks to something like this :

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9jlh420ffp8vhmlsptbj.png)

## MySQL Database with Docker
First, let's start with the database, we are going to run a MySQL container with our user and password, and then go inside the container to create our schema, table and some dummy data.
Our schema will have only on table "players" :


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s4lffzzwbzxwq3gbggb9.png)

Before we run the database container, we need to create a network on which our containers will be running, in fact when containers are created by Docker they are isolated and do not communicate with each other, this why we need to specify that the containers are on the same network in order to enable communication between them.
When we create a new network, Docker by default will create it as a bridge netwrok, to do this let's run this command :

> docker network create mynet

the output of this command should be a long alhphanumeric string (f9bc75b98986ac....)
You can type `docker network ls` and you will see your network newly created, you should see something like this :

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/96kinswwy1vlbfrcs2d9.png)

Now that we have our network, let's run our MySQL container. For this, we are going to use the official MySQL Docker image, and execute this command :
`docker run --name playersdb --network mynet -e MYSQL_ROOT_PASSWORD=1234 -d mysql:latest`

The command above will run a container from mysql image `docker run` with the latest version `mysql:latest`, the name of the container will be playersdb `--name playersdb`, the container will run on a network mynet `--network mynet`, the root password will be '1234' `-e MYSQL_ROOT_PASSWORD=1234` (-e is used to determine the environment variables of the container) and the container will run in a detached mode (in the background) `-d`

Let's check our container now, using `docker ps` :

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yfja4hyqzh6ii3fzidad.png)
Cool, our database container is now up and running, what we need to do now is to connect to our instance and create the schema we will use in our application.
we could have used the port mapping by adding the option `-p 3306:3306` in the Docker command above and would have used mysql workbench to manage the database. Instead we will use docker, specifically the "docker exec" command to open a mysql cli session into the container. To do this we need to run this command :
`docker exec -it playersdb mysql -uroot -p1234`
here we should find ourselves in the mysql cli, now we can copy past the sql code below, it will create a schema **playersdb**, a user **juser*, our table **players** as described above and finally insert some data into our db : 

```
CREATE DATABASE `playersdb` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
CREATE USER 'juser'@'%' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON * . * TO 'juser'@'%';
FLUSH PRIVILEGES;

USE playersdb;

CREATE TABLE `players` (
  `id` int DEFAULT NULL,
  `lastname` varchar(255) DEFAULT NULL,
  `firstname` varchar(255) DEFAULT NULL,
  `team` varchar(255) DEFAULT NULL,
  `position` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

INSERT INTO `playersdb`.`players`
(`id`,
`lastname`,
`firstname`,
`team`,
`position`)
VALUES
(1,
'Ryad',
'Boudebouz',
'Algeria',
'RW'),
(2,
'Manuel',
'Ruicosta',
'Portugal',
'CM'),
(3,
'Fernando',
'Redondo',
'Argentina',
'DM'),
(4,
'Ricardo',
'Kaka',
'Brazil',
'CM'),
(5,
'Ronaldo',
'TheOnlyRonaldo',
'Brazil',
'CF'),
(6,
'Paolo',
'Maldini',
'Italy',
'CB');
```
In order to check if the sql script executed without issue, you can run a select query `select * from playersdb.players`, the result below should be displayed :

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h0a6o7j6deblthhtpvte.png)

## Our Java application
Now that our database, schema and data are all set, we can jump to our Java application.
This will be a basic application that will connect to the database using JDBC, and run a select query on players table and display the result.

Let's start by creating simple project using Maven :

```
mvn archetype:generate -DgroupId=com.players.app -DartifactId=players-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false
```
This command will create a simple Java project with an App.java class. The first thing we need to do is to modify our pom.xml by adding the jdbc driver for MySQL database as a dependency and the build plugins to generate a Jar file.


```
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>8.0.22</version>
</dependency>
```

```
<build>
  <plugins>
    <plugin>
      <artifactId>maven-assembly-plugin</artifactId>
        <configuration>
	  <archive>
	    <manifest>
	      <mainClass>com.players.app.App</mainClass>
	    </manifest>
          </archive>
          <descriptorRefs>
            <descriptorRef>jar-with dependencies</descriptorRef>
          </descriptorRefs>
	</configuration>
    </plugin>
    <plugin>
    <!-- Build an executable JAR -->
      <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
	  <version>3.1.0</version>
	    <configuration>
	      <archive>
	        <manifest>					 
                  <addClasspath>true</addClasspath>				 
                    <classpathPrefix>lib/</classpathPrefix>							 
                      <mainClass>com.players.app.App
                        </mainClass>
		</manifest>
	      </archive>
	    </configuration>
	  </plugin>
    </plugins>
</build>
```
Now, we can write some Java code, our application is a silly application which will connect to the playersdb database (we ran on a container previously), execute an sql query and display the result,
This is the code of our Java class, you will find the github url at the end of this post.

```
public class App {
	// in the dbhost the first playersdb is the container name and the second one is the database name
	private static String connString = "jdbc:mysql://playersdb:3306/playersdb";
	private static String username = "juser";
	private static String password = "password";
	private static Connection conn;
	private static String sql_req = "select * from players";
	private static Statement stmt;
	private static ResultSet results;

	public static void main(String[] args) {

		try {
			conn = DriverManager.getConnection(dbhost, username, password);
			System.out.println("Connecting...");
			System.out.println("Connected !!");

			stmt = conn.createStatement();
			results = stmt.executeQuery(sql_req);

			while (results.next()) {
				System.out.println("Id = " + results.getInt("id") + "\n" + "FirstName = "
						+ results.getString("firstname") + "\n" + "LastName = " + results.getString("lastname") + "\n"
						+ "Team = " + results.getString("team") + "Position = " + results.getString("position"));

			}
		} catch (SQLException e) {
			System.out.println("Cannot connect to database connection");
			e.printStackTrace();
		}
	}
}
```

> Tip : we can test our java application locally, to do this all we need to do is run the mysql container with a port mapping option -p 3306:3306, and replace the host name in connection string by localhost or 127.0.0.1, which will give us **jdbc:mysql://localhost:3306/playersdb**

Once this is done, we can run maven to generate the Jar file, to do this you can run maven from your IDE (Eclipse, IntelliJ, VScode...) or you can simply open your terminal (CMD in windows, terminal in linux...) go to the project folder and run :
`mvn assembly:single`
This will build the project and will create a Jar file in the target folder. This is what you should see after the build finish:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iquny7lwxemq711rj0t9.png)  
You can see that a Jar file with the name **players-app-1.0-jar-with-dependencies.jar** has been created.

## Our java application containerization with Docker
At this point, we have our MySQL container ready, we have our jar file as well, all we need now, is a container in which our jar will be executed. For this let's create a new folder, copy in it the Jar file we created above, for simplification let's rename it players.jar. In the same folder let create a file called dockerfile, this file will contains the instructions to create a Docker image with our specific needs, in this case, an image that contains our jar file which will be executed every time we run the container from this image.
Let's open our dockerfile with a text editor and paste into it those instructions :

```
FROM openjdk:11

COPY /players.jar /players.jar

CMD ["java", "-jar", "/players.jar"]

```
So what those instructions means ? the FROM instruction set the base image, a valid Docker file must start with a FROM.
Then COPY will copy our jar file into the image.
And finally CMD, this instruction will provides defaults for an executing container, in our case, every time we run the container the command `java -jar players.jar` will be executed, this command we usually use to execute a jar file.

Once our dokcerfile created, we can create our image by executing this command :
`docker build -t playersapp:1.0 .` 
This command will build our image file from the dockerfile.

Now we can see that our image **playersapp** with the tag 1.0 is available, to check this we need to run `docker images` :

![docker images](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nlca64en9dvzxind57dc.png)

We are almost there !! Now, all will need to do is to run a container from our image **playersapp:1.0**, this container need to be in the same network than our MySQL container, so let's run this command :
`docker run --name playersapp --network mynet playersapp:1.0`
And now we can see the list of the players we created before in this post :

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pukfqyikropnfafufrob.png)

## Conclusion
We have reached the end of this tutorial, although the application is very simple, the objective here is have an idea about what cloud-native is, and a step-by-step to follow to run a java program from a container.

In the next post, our application will evolve into a spring boot application, and we will use docker compose. So see you soon :v: 


- GitHub repo of the players app : [players-app](https://github.com/MalikooR/players-app)


- If you want to learn more a about cloud native :
      - This book : [cloud-native-patterns](https://www.manning.com/books/cloud-native-patterns)
      - [Microsoft docs Introduction to cloud-native](https://docs.microsoft.com/en-us/dotnet/architecture/cloud-native/introduction)
- To learn Docker, the official doc is very good : [docker docs](https://docs.docker.com/)