Monday, 27 March 2017

Java Concurrency - Multi Threading with ExecutorService

In this post I'll look how the ExeutorService can be used to perform multi threaded asynchronous tasks. I'll begin by looking at the traditional approach of creating threads directly and then move on to examine the ExecutorService and how it can be used to simplify things.

Instantiating Threads Directly

Prior to the Executor API developers were responsible for instantiating and managing threads directly. Lets look at a simple example below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
   /**
    * Call 2 expensive methods on separate threads 
    *    
    * @throws InterruptedException 
    */
    public void doMultiThreadedWork() throws InterruptedException {
  
        /* create Runnable using anonymous inner class */
        Thread t1 = new Thread(new Runnable() {   
            public void run() {
                System.out.println("starting expensive task thread t1");
                doSomethingExpensive();    
                System.out.println("finished expensive task thread t1");
            }
        });
  
        /* start processing on new threads */
        t1.start();            
  
        /* block current thread until t1 has finished */
        t1.join();
    }
Simple Thread example

We begin by creating a new Thread object t1 and passing a Runnable to its constructor. Runnable is an interface with a single abstract method public void run(). To create a Thread we need to supply an implementation of the Runnable interface and in this instance we do that with an anonymous inner class. The run method contains logic that will be executed by the Thread when it is started. Note that if the code inside run throws a checked Exception it must be caught and handled inside the run method.

On line 18 the Thread is started by calling its start method. The JVM spawns a new process and executes the run() method in the context of the newly created Thread.  Note: you should be careful not to call the run() method directly as this will cause the method to execute in the context of the current thread and will result in single threaded behaviour.

On line 21 we call the join method to block the main thread execution until Thread t1 has terminated. This is only necessary if you want the main thread to wait for the spawned thread to terminate. Often this is not necessary, but for sake of our sample code we want to allow t1 to complete before continuing.

Introducing the Executor Service

Dealing with threads directly is all well and good but Oracle have made things a little easier by providing a layer of abstraction via its Executor API.  An Executor allows you to process tasks asynchronously and in parallel without having to deal with threads directly.

Creating an Executor

The Executors factory class is used to create an instance of an Executor, either an ExecutorService or a ScheduledExecutorService. Some of the most common types of Executors are described below.
  • Executors.newCachedThreadPool() - An ExecutorService with a thread pool that creates threads as required but reuses previously created threads as they become available.
  • Executors.newFixedThreadPool(int numThreads) - An ExecutorService that has a thread pool with a fixed number of threads. This is the maximum number of threads that can be active in the ExecutorService at any one time. If the number of requests submitted to the pool exceeds the pool size, requests are queued until a thread becomes available. 
  • Executors.newScheduledThreadPool(int numThreads) - A ScheduledExecutorService with a thread pool that is used to run tasks periodically or after a specified delay.
  • Executors.newSingleThreadExecutor() - An ExecutorService that uses a single thread. Tasks submitted to this ExecutorService will be executed one at a time and in the order submitted. 
  • Executors.newSingleThreadScheduledExecutor() - An ExecutorService that uses a single thread to execute tasks periodically or after a specified delay. 

Below is an example of creating a simple fixed thread pool ExecutorService with a pool size of 2. I'll use this ExecutorService in the following sections. 

1
    ExecutorService executorService = Executors.newFixedThreadPool(2);
Creating an ExecutorService using Executor factory method

Using an Executor

In the following sections I'm going to look at the various methods available on the ExecutorService for executing tasks asynchronously.

execute(Runnable)

The execute method takes a Runnable and is very similar to the simple Thread example we looked at earlier. The execute method is useful when you want to run a task and are not concern about checking its status or obtaining a result. Think of it as a means of invoking a fire and forget asynchronous task.

1
2
3
4
5
    executorService.execute(()->{
        System.out.println(String.format("starting expensive task thread %s", 
        Thread.currentThread().getName()));
        doSomethingExpensive();    
    }
Execute Runnable with ExecutorService

The example above creates a Runnable as a lambda expression and passes it to the execute method. The Runnable will be executed as soon as a thread is available from the ExecutorService threadpool.

Future<?> submit(Runnable)

The submit method also takes a Runnable but differs from the execute method in that it returns a Future. A Future is an object that represents a pending response form an asynchronous task. Think of it as a handle that can be used to check the status of the task or retrieve its result when the task completes. Futures use generics to allow the user to specify the return type of the task. However, given that a Runnables run method does not return a value (return type void), the Future holds the status of the task rather than a pending result. This is represented as Future<?> as shown in the example below.

1
2
3
4
5
    Future<?> taskStatus = executorService.execute(()->{
          System.out.println(String.format("starting expensive task thread %s", 
                                            Thread.currentThread().getName()));
          doSomethingExpensive();    
    }
                                                     Submit Runnable using lambda expression

The submit(Runnable) method is useful when you want to run a task that doesn't return a value but you'd like to check the status of the task after its been submitted to the ExecutorService.

Checking Task Status

Future provide a few methods that allow you to check the status of a task that's been submitted to the ExecutorService. The isCancelled method checks if a submitted task has already been cancelled. The isDone method allows you to check if the submitted task has completed. isDone will return true regardless of whether a task completed successfully, unsuccessfully or was cancelled. Finally, the cancel method can be used to cancel a submitted task. A boolean parameter indicates whether the task should be interrupted after its already been started.


1
2
3
4
5
6
7
    /* check if both tasks have completed - if not sleep current thread 
     * for 1 second and check again
     */
    while(!task1Future.isDone() || !task2Future.isDone()){
        System.out.println("Task 1 and Task 2 are not yet complete....sleeping");
 Thread.sleep(1000);
    }
Future isDone method used to check status of task 

Future<T> submit(Callable)

The submit method is overloaded to take a Callable as well as a Runnable. A Callable is similar to Runnable in that it represents a task that can be executed on another thread, but differs in that it returns a value and can throw a checked Exception. The Callable interface has the single abstract method public T call() throws Exception and like Runnable can be implemented with an anonymous inner class or functional interface. The return type of the call method is used to type the Future returned by the ExecutorService. There are two examples below, one submitting a Callable using an anonymous inner class and the other using a lambda expression.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    Future<Double> task1Future = executorService.submit(new Callable<Double>() {
  
        public Double call() throws Exception {     
   
            System.out.println(String.format("starting expensive task thread %s", 
                                              Thread.currentThread().getName()));
            Double returnedValue = someExpensiveRemoteCall();    
   
            return returnedValue;
        }           
    });
Submit Callable using anonymous inner class 

1
2
3
4
5
6
7
8
    Future<Double> task2Future = executorService.submit(()->{
  
        System.out.println(String.format("starting expensive task thread %s", 
                                           Thread.currentThread().getName()));
        Double returnedValue = someExpensiveRemoteCall();    
  
        return returnedValue;
    });
Submit Callable using lambda

The example above creates a Callable as a lambda and passes it to the execute method. The Callable will be executed as soon as a thread is available.

Retrieving a Result  from a Future

When we submit a Callable to the ExecutorService we receive a Future with the return type of the call method. In the example above the call method returns a Double so the Future returned is Future<Double>. One way of retrieving the result from a Future is by calling its get method. This method will block indefinitely waiting on the submitted task to complete. If the task doesn't complete or takes a long time to complete, the main application thread will remain blocked.

Waiting indefinitely for a result is often not ideal. We'd rather have more control over how we retrieve the result and take some action if a task does not complete within a acceptable period of time. Luckily there is an overloaded version of the get method that takes a time value and time unit. This method waits for the specified period of time and if the task is not complete and a result not available, throws a TimeoutException.

1
2
    Double value1 = task1Future.get();
    Double value2 = task2Future.get(4,  TimeUnit.SECONDS); // throws TimeoutException
                                                                                     Get result from Callable


Submit Multiple Callables

As well as supporting the submission of a single Callable, the ExecutorService allows you to submit a Collection of Callables using the invokeAll method. As you might expect, instead of receiving a single Future response, a Collection of Futures is returned. A Future is returned representing the pending result of each submitted task.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
    Collection<Callable<Double>> callables = new ArrayList<>();
    IntStream.rangeClosed(1, 8).forEach(i-> {
        callables.add(createCallable());
    });
   
    /* invoke all supplied Callables */ 
    List<Future<Double>> taskFutureList = executorService.invokeAll(callables);
   
    /* call get on Futures to retrieve result when it becomes available.
     * If specified period elapses before result is returned a TimeoutException
     * is thrown
     */
    for (Future<Double> future : taskFutureList) {
    
        /* get Double result from Future when it becomes available */
        Double value = future.get(4, TimeUnit.SECONDS);
        System.out.println(String.format("TaskFuture returned value %s", value)); 
    }
Invoking a Collection of Callables

The sample code above submits 8 Callables to the ExecutorService and retrieves a List containing 8 Futures. The Futures returned are in the same order as the Callables were submitted to the ExecutorService. Note that submitting a Collection of Callables will require that the size of the thread pool is tweaked if we want  most or all of the submitted tasks can be executed in parallel. For the example above we'd need a thread pool with 8 threads to run all tasks in parallel  

Shutting Down the ExecutorService

After all tasks have completed its important to shut down the ExecutorService gracefully so that resources used by the underlying thread pool are reclaimed by the JVM. There are 2 methods available, shutdown and shutdownNow. The shutDown method triggers a shutdown of the ExecutorService, allowing currently processing tasks to finish but rejecting newly submitted tasks.

shutDownNow also triggers a shutdown of the ExecutorService, but does not allow currently executing tasks to complete, attempting to terminate them immediately. shutDownNow returns a list of tasks that were queued for execution when the shutdown was initiated.  To avoid potential resource leaks its important that the ExecutorService is shut down inside a finally block.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
    ExecutorService executorService = null;
  
    try{  
        executorService = Executors.newFixedThreadPool(2);
   
        executorService.execute(()->{
            System.out.println(String.format("starting expensive task thread %s", 
                                               Thread.currentThread().getName()));
            doSomethingExpensive();    
        });
      
    }
    finally{
        executorService.shutdown();    
    }
ExecutorService shut down in finally block

Source Code  

The source code used in this post is available on Github. Feel free to pull the code and have a play around with it. As always, feel free to post comments, questions or suggestions below. 

Tuesday, 4 October 2016

Apache CXF- Contract First Web Services

I wrote a post a few years ago detailing a step by step guide to building a contract first web service using Spring.  I recently started working with Apache CXF and thought it would be worth putting together a more up to date post, this time using CXF.
We'll create a fictitious Account Service that takes a single account number parameter and returns associated account information. Although the sample service will be very simple, the approach taken should provide you with a solid foundation upon which to build real world services.

Source Code

The full source code for this post is available on GitHub so feel free to have a look. You may find it useful to have the code locally as you work through this post.

Domain Model Definition

We'll begin by defining the service domain model as an XML Schema Definition (XSD). The schema will define the entities that our Account service will use. We'll begin by defining the core Account entity - the diagram below shows the XSD snippet that defines the Account as well as a visual representation taken from XML Spy.

Next we'll define the AccountDetailsRequest type that will be used as a parameter to the Account service.

Finally we'll define the AccountDetailsResponse type that will be used as the return type from the Account service.

The full schema definition is shown below.

 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
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://com/blog/samples/webservices/accountservice" xmlns:account="http://webservices.samples.blog.com" targetNamespace="http://com/blog/samples/webservices/accountservice" elementFormDefault="qualified">
 <xsd:complexType name="Account">
  <xsd:sequence>
   <xsd:element name="AccountNumber" type="xsd:string"/>
   <xsd:element name="AccountName" type="xsd:string"/>
   <xsd:element name="AccountBalance" type="xsd:double"/>
   <xsd:element name="AccountStatus" type="EnumAccountStatus"/>
  </xsd:sequence>
 </xsd:complexType> 
 <xsd:simpleType name="EnumAccountStatus">
  <xsd:restriction base="xsd:string">
   <xsd:enumeration value="Active"/>
   <xsd:enumeration value="Inactive"/>
  </xsd:restriction>
 </xsd:simpleType>
 <xsd:element name="AccountDetailsRequest">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:element name="accountNumber" type="xsd:string"/>
   </xsd:sequence>
  </xsd:complexType>
 </xsd:element>
 <xsd:element name="AccountDetailsResponse">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:element name="AccountDetails" type="Account"/>
   </xsd:sequence>
  </xsd:complexType>
 </xsd:element>
</xsd:schema>

Service Contract Definition

Next we'll define the public facing service contract using a WSDL. A WSDL is an XML document that describes a SOAP Web Service and how clients can interact with it. WSDLs are a great way to describe service contracts because they are easy to interpret by developers and can be used by tooling to generate many useful artefacts, some of which we'll look at later.  The Account Service WSDL is defined as follows.

 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
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap12/"
 xmlns:tns="http://www.briansjavablog.com/Accounts/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Accounts"
 targetNamespace="http://www.briansjavablog.com/Accounts/"
 xmlns:accounts="http://com/blog/samples/webservices/accountservice">
 <wsdl:types>
  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <xsd:import namespace="http://com/blog/samples/webservices/accountservice"
    schemaLocation="../schema/AccountsService.xsd">
   </xsd:import>
  </xsd:schema>
 </wsdl:types>
 <wsdl:message name="AccountDetailsRequest">
  <wsdl:part element="accounts:AccountDetailsRequest" name="parameters" />
 </wsdl:message>
 <wsdl:message name="AccountDetailsResponse">
  <wsdl:part element="accounts:AccountDetailsResponse" name="parameters" />
 </wsdl:message>
 <wsdl:portType name="Accounts">
  <wsdl:operation name="GetAccountDetails">
   <wsdl:input message="tns:AccountDetailsRequest" />
   <wsdl:output message="tns:AccountDetailsResponse" />
  </wsdl:operation>
 </wsdl:portType>
 <wsdl:binding name="AccountsServiceSoapBinding" type="tns:Accounts">
  <soap:binding style="document"
   transport="http://schemas.xmlsoap.org/soap/http" />
  <wsdl:operation name="GetAccountDetails">
   <soap:operation
    soapAction="http://www.briansjavablog.com/Accounts/GetAccountDetails" />
   <wsdl:input>
    <soap:body use="literal" />
   </wsdl:input>
   <wsdl:output>
    <soap:body use="literal" />
   </wsdl:output>
  </wsdl:operation>
 </wsdl:binding>
 <wsdl:service name="AccountsService">
  <wsdl:port binding="tns:AccountsServiceSoapBinding" name="AccountsPort">
   <soap:address
    location="http://localhost:8080/apache-cfx-demo/services/accounts" />
  </wsdl:port>
 </wsdl:service>
</wsdl:definitions>
                                                                                    
The WSDL is composed of 5 main parts
  • Types - the <wsdl:types> section defines the domain model associated with the service. The types are defined using XSD and can be defined in the WSDL itself or imported from a separate XSD. On line 9 above we import the schema definition file we created earlier.
  • Message - the <wsdl:message> is used to define the request and response messages handled by the service. The nested <wsdl:part> section defines the domain type that will be used to form the messages.
  • PortType - the <wsdl:portType> defines the service operations that are exposed to clients, parameters required to invoke the operations and response types returned. 
  • Binding - the <wsdl:binding> section defines the protocol and data format. 
    • The binding type attribute refers to the portType defined earlier in the WSDL. 
    • The soap binding style can be either RPC or document. In this instance we've chosen document. 
    • The transport attribute indicates that the service will be exposed over HTTP. Other less common options include JMS and SMTP.
    • The operation element defines each operation that we exposed through the portType.
    • Binding - the <wsdl:binding> section defines the protocol and data format. 
  • Service - the <wsd;:service> defines the exposed service using the portType and binding we defined above.  

POM Configuration

Now that we've defined the WSDL its time to configure the POM to use the Apache CXF code generation plugin. The plugin invokes a WSDL2Java process which parses the WSDL and generates a service endpoint interface and associated domain objects. The plugin configuration is shown below. Note that I've omitted the rest of the POM definition for brevity. To see the full definition pull the sample code from github.

 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
          <plugins>
      <plugin>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-codegen-plugin</artifactId>
  <version>3.1.7</version>
  <executions>
     <execution>
        <id>generate-sources</id>
   <phase>generate-sources</phase>
   <configuration>
      <sourceRoot>src/generated/java</sourceRoot>
      <wsdlOptions>
         <wsdlOption>
            <wsdl>${basedir}/src/main/resources/wsdl/Accounts.wsdl</wsdl>
         </wsdlOption>
      </wsdlOptions>
   </configuration>
   <goals>
      <goal>wsdl2java</goal>
   </goals>
     </execution>
  </executions>
      </plugin>
          </plugins>
                                                                          
The WSDL path on line 14 tells CXF what WSDL to run WSDL2Java with. The sourceRoot configuration on line 11 is the fully qualified package name that the generated class will be copied to. Its a good idea to put these in src/generated/java so that its obvious to other developers that the package contains generated code.

Code Generation

We're now ready to generate the Service Endpoint Interface and domain objects from the WSDL. To run the code generation simply open a command window and run mvn generate-sources



Refresh the IDE workspace and you will see 2 new packages. The package names are based on the namespaces specified in the WSDL. The contents of both packages is described below.
  • com.blog.samples.webservices.accountservice - contains 4 service domain objects, Account, AccountDetailsRequest, AccountDetailsResponse and EnumAccountStatus. These 4 types represent the service request and response. ObjectFactory is a helper class for creating new instances of the domain types and package-info.java applies XML namespace metadata to all domain classes in the package.
  • com.briansjavablog.accounts - contains the Service Endpoint Interface Accounts.java. This interface is a Java representation of the service operation we defined in the WSDL. The contents of this class are covered in detail below. AccountsService.java is a web service client and can be used to invoke the service.

Service Endpoint Interface

The Service endpoint interface that was generated in the previous step is defined below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/**
 * This class was generated by Apache CXF 3.1.7
 * 2016-09-29T07:57:42.236+01:00
 * Generated source version: 3.1.7
 * 
 */
@WebService(targetNamespace = "http://www.briansjavablog.com/Accounts/", name = "Accounts")
@XmlSeeAlso({com.blog.samples.webservices.accountservice.ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface Accounts {

    @WebMethod(operationName = "GetAccountDetails", action = "http://www.briansjavablog.com/Accounts/GetAccountDetails")
    @WebResult(name = "AccountDetailsResponse", targetNamespace = "http://com/blog/samples/webservices/accountservice", partName = "parameters")
    public com.blog.samples.webservices.accountservice.AccountDetailsResponse getAccountDetails(
        @WebParam(partName = "parameters", name = "AccountDetailsRequest", targetNamespace = "http://com/blog/samples/webservices/accountservice")
        com.blog.samples.webservices.accountservice.AccountDetailsRequest parameters
    );
} 
  • @WebService - Marks the class as defining a Web Service interface from a WSDL. The namespace should match the namespace defined in the WSDL and the name should match the WSDL PortType.
  • @XmlSeeeAlso - Lets JAXB know what other classes need to be registered with the JAXB context for serialization and deserialization. 
  • @SoapBinding - Describes mapping from web service operations to SOAP protocol
  • @WebMethod - Maps a service operation to a Java method. The operation name references the operation defined in the WSDL and the target namespace uses the namespace associated with the WSDL operation.  
  • @WebResult - Maps a service operation response message to a Java return type. The name refers to the response message name defined in the WSDL. The target namespace uses the namespace associated with the WSDL message and the partName refers to wdl:part name in the WSDL.
  • @WebParam - Maps a service operation request message to a Java parameter type. The name refers to the request message name defined in the WSDL. The target namespace uses the namespace associated with the WSDL message and the partName refers to wdl:part name in the WSDL. 

Service Endpoint Implementation

Now that we've generated the Service Endpoint Interface, its time to create an implementation. The endpoint implementation class will be called by the CXF framework after it has deserialized the incoming SOAP body and figured out which endpoint method should handle the request. Our simple service exposes a single operation, but for services with multiple operations CXF uses the request payload to determine which endpoint method to call. Creating an endpoint implementation is straight forward and simply requires a class that implements the Service Endpoint Interface as shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@WebService(portName = "Accounts", serviceName = "Accounts", 
            endpointInterface = "com.briansjavablog.accounts.Accounts", 
            targetNamespace = "http://www.briansjavablog.com/Accounts/")
public class AccountServiceEndpoint implements Accounts {

    private AccountService accountService;

    @Autowired
    public AccountServiceEndpoint(AccountService accountService) {
        this.accountService = accountService;
    }

    @Override
    public AccountDetailsResponse getAccountDetails(AccountDetailsRequest parameters) {

        return accountService.getAccountDetails(parameters);
    }
}

The @WebService annotation marks this class as implementing a web service endpoint. The attributes portName, serviceName and targetNamespace should all match their equivalent in the WSDL definition. The enpointInterface refers to the generated Service Endpoint Interface class we looked at earlier. The AccountServiceEndpoint constructor takes a Spring injected AccountService as an argument. The getAccountDetails method is an implementation of the interface method defined on the Service Endpoint Interface class and uses the injected service to get the required account details.

Account Service 

The Account Service injected into the endpoint implementation above is trivial and simply returns hard coded values. I've split this out into a separate service class to demonstrate that its good practice to decouple endpoint processing logic from business logic. The only thing to note in this class is that we use the JAXB generated ObjectFactory to create an instance of the response type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Service
public class AccountServiceImpl implements AccountService {

    @Override
    public AccountDetailsResponse getAccountDetails(AccountDetailsRequest parameters) {

        ObjectFactory factory = new ObjectFactory();
        AccountDetailsResponse response = factory.createAccountDetailsResponse();
  
        Account account = factory.createAccount();
        account.setAccountNumber("12345");
        account.setAccountStatus(EnumAccountStatus.ACTIVE);
        account.setAccountName("Joe Bloggs");
        account.setAccountBalance(3400);
  
        response.setAccountDetails(account);  
        return response;
    }
}
                                                                                      

Spring XML Configuration 

The guys at CXF have made a considerable effort to ensure that CXF integrates nicely with the Spring framework. Of course CXF can be configured programatically without Spring, but if you're already using Spring for dependency injection (or anything else for that matter), it makes sense to use CXFs Spring support. This section describes how to configure the endpoint we created earlier so that it can be published to a Servlet container like Tomcat.

 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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jaxws="http://cxf.apache.org/jaxws"
    xsi:schemaLocation="http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

   <context:component-scan base-package="com.briansjavablog.accounts" />

   <jaxws:endpoint id="accountService"
                   implementor="com.briansjavablog.accounts.service.AccountServiceEndpoint"
                   address="/services/accounts">
    <jaxws:inInterceptors>
      <ref bean="loggingInInterceptor" />
    </jaxws:inInterceptors>
    <jaxws:outInterceptors>
      <ref bean="loggingOutInterceptor" />
    </jaxws:outInterceptors>
   </jaxws:endpoint>

   <bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
   <bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />

</beans>
  • Line 11 - Enable component scanning so that beans annotated as @Component and @Service are registered as Spring components. 
  • Lines 13 to 15 - Define endpoint configuration by specifying endpoint implementation class for implementor and the address of the exposed endpoint. Note that the address is relative to the application root.
  •  Lines 16 to 21 - Register endpoint interceptors for logging incoming SOAP request payload and outgoing SOAP response payload.
  • Line 24 - Create interceptor for logging inbound SOAP payloads. This interceptor is provided by CXF out of the box and is executed before the JAXB deserialization and endpoint implementation class is called. 
  • Line 24 - Create interceptor for logging outbound SOAP payloads. This interceptor is provided by CXF out of the box and is executed after endpoint service method is executed and the JAXB serialization and endpoint implementation class is called. 

Web.xml 

The final piece of configuration we need to run our service in a Servlet container is the web.xml. If you've done any kind of Java web development the web.xml definition below will look familiar.

 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
<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>WEB-INF/beans.xml</param-value>
    </context-param>

    <listener>
 <listener-class>
     org.springframework.web.context.ContextLoaderListener
 </listener-class>
    </listener>

    <servlet>
 <servlet-name>CXFServlet</servlet-name>
 <display-name>CXF Servlet</display-name>
 <servlet-class>
     org.apache.cxf.transport.servlet.CXFServlet
 </servlet-class>
 <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
 <servlet-name>CXFServlet</servlet-name>
 <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>
  • Line 8 to 11 - Provides a path to the Spring beans.xml configuration file. This file is used by the ContextLoaderListener described below.
  • Lines 13 to 17 - ContextLoaderListener creates the Spring application context on container startup. It uses the bean definitions defined in contextConfigLocation.
  • Lines 19 to 26 - Configures CXF provided Servlet to process incoming HTTP requests. 
  • Lines 28 to 31 -  Servlet mapping configures CXF Servlet to process all requests received at the application root.

Integration Test 

The next step is to add an integration test. Most projects now a days use some sort of continuous integration so its important to have integration test coverage that is run on every code push. We're going to create a simple happy path integration test that will deploy the service to a specified endpoint using Jetty, make a HTTP call to the endpoint and then perform some assertions on the response. This is a simple but effective end to end test that we can run locally or on a continuous integration environment, without having to worry about deploying to a standalone Servlet container.
We'll begin by looking at the Spring configuration that describes the dependencies we need to run our test.

 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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:jaxws="http://cxf.apache.org/jaxws"
 xsi:schemaLocation="
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

 
 <context:component-scan base-package="com.briansjavablog.accounts" />

 <jaxws:server id="accountService"
        address="http://localhost:8080/apache-cxf-demo/services/accounts">
     <jaxws:serviceBean>
  <bean class="com.briansjavablog.accounts.service.AccountServiceEndpoint" />
     </jaxws:serviceBean>
     <jaxws:inInterceptors>
  <ref bean="loggingInInterceptor" />
     </jaxws:inInterceptors>
     <jaxws:outInterceptors>
                <ref bean="loggingOutInterceptor" />
     </jaxws:outInterceptors>
 </jaxws:server>

 <jaxws:client id="testAccountServiceClient"
        address="http://localhost:8080/apache-cxf-demo/services/accounts"
        serviceClass="com.briansjavablog.accounts.Accounts">
     <jaxws:inInterceptors>
         <ref bean="loggingInInterceptor" />
     </jaxws:inInterceptors>
     <jaxws:outInterceptors>
                <ref bean="loggingOutInterceptor" />
     </jaxws:outInterceptors>
 </jaxws:client>

 <bean id="abstractLoggingInterceptor" abstract="true">
     <property name="prettyLogging" value="true" />
 </bean>
 <bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" parent="abstractLoggingInterceptor" />
 <bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor" parent="abstractLoggingInterceptor" />

</beans>
  • Line 12 - Configures component scanning so that beans annotated with @Component and @Service are registered with the bean factory.
  • Lines 14 to 15 - jaxws:server defines a server configuration that will launch Jetty to host the web service for the duration of the test. The address attribute specifies the local URL where the service will be deployed.
  • Lines 16 to 18 - Defines the endpoint class that will service incoming requests. This is the service endpoint implementation class we defined earlier.
  • Lines 19 to 24 - Logging interceptors are registered with the server to log inbound SOAP requests and outbound SOAP responses.
  •  Lines 27 to 29 - jaxws:client defines a web service client that we'll use to call the service. This client will be injected into our test class. The address attribute is the target URL of the service and the serviceClass is the Service Endpoint Interface that was generated earlier.
  • Lines 30 to 35 - Logging interceptors are registered with Client to log outbound SOAP requests and inbound SOAP responses.
  • Lines 38 to 42 - Defines inbound and outbound logging interceptors that are used by both the client and the server, The abstract interceptor is used to define a common property (prettyLogging) that is used by both logging interceptors.     
Finally we'll look at the test class definition. This class uses the test configuration above to create a web service client, a test server and deploy the application to the server.

 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
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration( {"classpath:beans-config-test.xml"} )
public class AccountServiceEndpointTest {

    @Autowired
    private Accounts accountsService;
    private AccountDetailsRequest accountDetailsRequest;
 
    @Before
    public void setUp() throws Exception {
  
        ObjectFactory objectFactory = new ObjectFactory();
 accountDetailsRequest = objectFactory.createAccountDetailsRequest();
 accountDetailsRequest.setAccountNumber("12345");
    }

    @Test
    public void testGetAccountDetails() throws Exception {
        AccountDetailsResponse response = accountsService.getAccountDetails(accountDetailsRequest);
        assertTrue(response.getAccountDetails()!= null);
        assertTrue(response.getAccountDetails().getAccountNumber().equals("12345"));
        assertTrue(response.getAccountDetails().getAccountName().equals("Joe Bloggs"));
        assertTrue(response.getAccountDetails().getAccountBalance() == 3400);
        assertTrue(response.getAccountDetails().getAccountStatus().equals(EnumAccountStatus.ACTIVE));
    }

}

  • Lines 1 & 2 - Define this as test that relies on Spring managed dependencies. The test application context is loaded form the beans-config-test.xml file we defined earlier. 
  • Lines 5 & 6 - Spring injects an instance of the Accounts Endpoint Service Interface that was generated from the WSDL. The injected instance is a client side proxy to the remote service and is constructed by Spring using the jaxws:client definition we defined in beans-config-test.xml. This is essentially a bridge out to the remote service deployed on Jetty.
  • Lines 9 to 15 - Setup method is called before the test method and is used to create a request object.
  • Lines 17 to 25 - Test method uses injected AccountService client side proxy to call the remote service deployed  on Jetty and performs a number of assertions on the deserialized response.

Running the Test

Running the test is straight forward. Simply fire up a command window and run the mvn test command. You should see the SOAP request and response payloads logged by both the client and the server as follows.

Running on Tomcat 

The service can be deployed to Tomcat manually or you can use the Tomcat maven plugin by running mvn tomcat:run-war from the command line. When the service is deployed we can test it on port 8080 using a SOAP client. We'll use the Eclipse Web Services Explorer to run a quick manual test to ensure the service is up and running.
  • In Eclipse launch the Web Services Explorer, enter the path to the WSDL as shown below and click Go.

  • Click the GetAccountDetails operation

  • Populate the account number parameter and click Go to call the service. In the bottom panel click the Source link to see the raw SOAP request and response payloads.

Wrapping Up

In this post we took a fairly detailed look at building and testing a contract first web service using Apache CXF. The service itself is very simple but the approach used should provide you with a solid grounding for building more complex services. The full source code is available on Github. As always, feel free to post comments, questions or suggestions below. Feedback is always welcome.

Thursday, 11 August 2016

Docker - Multi Container App

In my last post I used Docker to build and run a simple Spring Boot application. This post will take things a little further by introducing a second container, showing you how distinct components can be deployed in separate containers and how those containers can communicate.

We'll build a simple Spring Boot app with a REST endpoint that takes an incoming message and adds it to an ActiveMQ message queue. A second endpoint will use a receiver component to consume the next message from the queue and return it to the client.  The application itself is simple but it'll provide a pretty realistic use case for how you might use multiple Docker containers during development.
Fig 1.0 - Boot & ActiveMQ - Multi Container App

Application Components

I'm not going to cover the application code in a huge amount of detail but I'll describe the fundamentals components and how they fit together. The MessageController below exposes 2 endpoints, one for posting a message and one for retrieving a message.  I've injected a MessageSender and MessageReceiver which are used to publish messages to and retrieve messages from an ActiveMQ message broker.

 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
@RestController
public class MessageController {

    private final MessageSender messageSender;
    private final MessageReceiver messageReceiver;

    @Autowired
    public MessageController(MessageSender messageSender, MessageReceiver messageReceiver){
       this.messageSender = messageSender;
       this.messageReceiver = messageReceiver;
    }
 
    @RequestMapping(value = "/message/", method = RequestMethod.GET)
    public String retrieveMessage() {
 
       return messageReceiver.retrieveMessage();
    }
  
    @RequestMapping(value = { "/message" }, method = { RequestMethod.POST })
    public void publishMessage(@RequestBody String message) {

       SimpleMessage simpleMessage = new SimpleMessage(message, new Date());
       messageSender.publishMessage(simpleMessage);
    }

}

MessageSender uses a jmsTemplate to send a message to the queue. This method is called from the REST controller to publish the message received via the HTTP endpoint.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Component
@Slf4j
public class MessageSender {

    private final JmsTemplate jmsTemplate;

    @Autowired
    public MessageSender(JmsTemplate jmsTemplate, MessageConverter messageConverter) {
       this.jmsTemplate = jmsTemplate;
       this.jmsTemplate.setMessageConverter(messageConverter);  
    }

    public void publishMessage(SimpleMessage simpleMessage){
  
       log.info("Sending message to queue: {}", simpleMessage.toString());
       jmsTemplate.convertAndSend("TestQueue", simpleMessage);
    }
 
}

MesssageReceiver uses a JmsTemplate to retrieve a message from the queue. The retrieveMessage method is invoked by the REST controller which will return the message to the client.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Component
@Slf4j
public class MessageReceiver {

    private final JmsTemplate jmsTemplate;

    @Autowired
    public MessageReceiver(JmsTemplate jmsTemplate, MessageConverter messageConverter) {
       this.jmsTemplate = jmsTemplate;
       this.jmsTemplate.setMessageConverter(messageConverter);
    }

    public String retrieveMessage() {

       String message = (String) jmsTemplate.receiveAndConvert("TestQueue");
       log.info("Retrieved message from queue: {}", message);
       return message;
    }
}

SimpleMessageConverter is used by the MessageSender and MessageReceiver components to do some simple transformation. MessageSender uses this class to convert a simple message POJO to a String before publishing to the queue. MessageReceiver uses this class to extract the message String from the Message returned from the queue.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Component
public class SimpleMessageConverter implements MessageConverter {

    @Override
    public Message toMessage(Object messageObject, Session session) throws JMSException, MessageConversionException {

       SimpleMessage simpleMessage = (SimpleMessage) messageObject;
       MapMessage message = session.createMapMessage();
       message.setString("message", simpleMessage.toString());
       return message;
    }

    @Override
    public Object fromMessage(Message message) throws JMSException, MessageConversionException {

       MapMessage mapMessage = (MapMessage) message;
       return mapMessage.getString("message");
    }

The application.properties below contains a single property that defines the message broker URL. We'll see later that the host name value (activemq) is significant when we come to linking Docker containers.

Running ActiveMQ

Now that we've defined the app lets look at setting up ActiveMQ. We need an ActiveMQ instance with a single queue definition called TestQueue. Remember that we reference TestQueue  as the destination in MessageSender and MessageReceiver. To save you time I've already created such a Docker image and pushed it to DockerHub.  Running the command below will pull the image from DockerHub and start a container.

1
docker run --name activemq -p 8161:8161 briansjavablog/activemq

The --name flag allows us to provide a name for the container we're starting. We'll use this name later when we are linking the app container to this ActiveMQ container.  The -p 8161:8161 tells Docker to expose port 8161 to the host machine on port 8161. This is required so that we can access the message broker admin console when it starts and check that the message queue is configured and ready to use. When the container starts up open a browser and go to http://localhost:8161/admin/queues.jsp. You should see a single queue defined as shown below.

Fig 1.1 - ActiveMQ Queue Definition

Building the Application Image

We'll start by defining a simple Dockerfile that takes a pre built JAR from the target directory and adds it to the image. The ENTRYPOINT tells Docker to run the executable JAR on container startup.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
FROM anapsix/docker-oracle-java8

# Creating working directory
WORKDIR /app

# Add src to working directory
ADD target/docker-boot-activemq-0.1.0.jar /app/docker-boot-activemq-0.1.0.jar

# Start app
ENTRYPOINT ["java","-jar","/app/docker-boot-activemq-0.1.0.jar"]     

To create an image form the Dockerfile simply run docker build -t "docker-activemq" . as shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
brianh@brianh-VirtualBox:~/apps/docker-boot-activemq$ docker build -t "docker-activemq" .
Sending build context to Docker daemon 19.98 MB
Step 1 : FROM anapsix/docker-oracle-java8
 ---> a8a9dcb0ac64
Step 2 : WORKDIR /app
 ---> Using cache
 ---> 4a5482ed5d76
Step 3 : ADD target/docker-boot-activemq-0.1.0.jar /app/docker-boot-activemq-0.1.0.jar
 ---> 4dd383ddc5aa
Removing intermediate container b720dc09ee95
Step 4 : ENTRYPOINT java -jar /app/docker-boot-activemq-0.1.0.jar
 ---> Running in 398ca91a7205
 ---> 74e632bb7020
Removing intermediate container 398ca91a7205
Successfully built 74e632bb7020


Linking Containers

If you've followed the steps so far, you should have an ActiveMQ container up and running and an application image built and ready to run. We're now going to start a second container to run our application and link it to the ActiveMQ container we started earlier.

1
docker run -p 8080:8080 --link activemq:activemq docker-activemq
  • -p 8080:8080 exposes port 8080 on the container to port 8080 on the host machine. This is required as the Boot app will start on container port 8080 as soon as the image is started.
  • --link activemq:activemq defines the container we want to link to and an alias that we can use to reference it. In this case we are linking to the activemq container we started earlier.
  • docker-activemq is the name of the application Docker image we built earlier.,
When we defined the message broker URL earlier in application.properties, we set the URL as follows.

1
spring.activemq.brokerurl=tcp://activemq:61616

So how does the app container know how to resolve the above URL to a service exposed in another container? To see how, we're going to open a shell in the app container and have a look. After the app container has started run the following from another terminal window. Note that the value inside quotes is the ID of the application container. To get the container ID run docker ps.

1
docker exec -it "1a4db2b586c3" bash

You should now have shell access to the app image and your current working directory should be /app. Remember /app is the working directory we created earlier to add the JAR to. Now lets take a look at the hosts file. Simply run vi /etc/hosts as shown below.

1
2
3
root@1a4db2b586c3:/app# ls
docker-boot-activemq-0.1.0.jar
root@1a4db2b586c3:/app# vi /etc/hosts

On container startup when we specified the --link command we supplied the container we wanted to link to and an alias that we'd use to reference it. Docker used that alias to create a hosts entry (line 7) to map the container alias activemq to the IP of the activemq container.

1
2
3
4
5
6
7
8
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2      activemq fa0b6f943d62
172.17.0.3      1a4db2b586c3

When the application in one container attempts to connect to ActivceMQ in the other container, the broker URL tcp://activemq:61616 will resolve to tcp://172.17.0.2:61616. To check that this IP address is indeed the IP address of the ActiveMQ container run the following command.

1
2
brianh@brianh-VirtualBox:~$ docker inspect --format '{{ .NetworkSettings.IPAddress }}' fa0b6f943d62
172.17.0.2


Testing the Application

Now that we've started and established a link between the application and ActiveMQ containers, its time to test it. Post a message to the MessageController endpoint using CURL as follows.

1
curl -i -H "Content-Type: application/json" -X POST -d "Test Message 1" localhost:8080/message/

In the app logs you should see a message saying that the message was added to the queue.

1
2016-08-10 17:44:52.789  INFO 1 --- [nio-8080-exec-1] com.blog.sample.app.mq.MessageSender     : Sending message to queue: SimpleMessage(message=Test Message 1, creationTime=Wed Aug 10 17:44:52 UTC 2016)

You can confirm that the message is indeed on the queue by opening the ActiveMQ admin console.
To retrieve the message from the queue, run curl -i localhost:8080/message/.

1
2
3
4
5
6
7
brianh@brianh-VirtualBox:~$ curl -i localhost:8080/message/
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 80
Date: Thu, 11 Aug 2016 06:44:51 GMT

SimpleMessage(message=Test Message 1, creationTime=Wed Aug 10 17:44:52 UTC 2016)brianh@brianh-VirtualBox:~$ 


Source Code

The full source code for this post can be found on Github and the ActiveMQ image is available on DockerHub. As always, if you have any questions, comments or suggestions please leave a note below.

Friday, 22 July 2016

Docker & Spring Boot

Docker allows you to package an application with its dependencies, into a light weight, portable container that can run on almost any environment.  You can think of a Docker container as a run time, a mini virtual machine that encapsulates your application and its dependencies.
In order to run a container you need a Docker image. An image is like a template that defines everything that will exist within the container. You can almost think of an container as a run time instance of the image it was created from. In this post we'll define and build 3 slightly different Docker images that run a simple Java app.

Installing Docker 

If you haven't already done so you'll need to install Docker. The official documentation is pretty good so following it step by step should see you up and running in about 15 minutes.
I've installed Docker on Windows and Ubuntu but to be honest I prefer running it on Ubuntu and have found it a bit more reliable than with Docker Toolbox on Windows.    
Follow the links above to install docker on your OS of choice. Once you're done, open a terminal window and run docker run hello-world to check that your docker install is working as expected.   

 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
brianh@brianh-VirtualBox:~/apps$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world

c04b14da8d14: Already exists 
Digest: sha256:0256e8a36e2070f7bf2d0b0763dbabdd67798512411de4cdcf9431a1feb60fd9
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker Hub account:
 https://hub.docker.com

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

This command pulls the hello-world image from the Docker hub repository and uses the image to start a container. If you see the output shown above you'll know your docker installation was successful.

Sample Code 

We're going to look at 3 slightly different ways of building a docker image to run a simple Spring Boot app. We'll start a container from each of the 3 images and call the application health check to make sure the app is up and running.

The Boot app itself couldn't be simpler, consisting of just an Application class annotated with @SpringBootApplication. This is enough to enable auto configuration and act as the application entry point. We don't even need to define our own health check as Boot provides one out of the box. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.blog.samples.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
   
}



Dockerfile Definition

A Docker file is a set of instructions or steps that tells Docker how to build an image. The Dockerfile below defines steps to build, package and run our app.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
FROM anapsix/docker-oracle-java8

# Install maven
RUN apt-get update -y
RUN apt-get install -y maven

# Creating working directory
WORKDIR /app

# Add src to working directory
ADD pom.xml /app/pom.xml
ADD src /app/src

# Build JAR
RUN mvn package -DskipTests=true

# Start app
ENTRYPOINT ["java","-jar","/app/target/docker-sample-1-0.1.0.jar"]

We'll walk through the Dockerfile  line by line and explain whats happening.
  • Line 1 - FROM instruction tells Docker what base image we want to use as a starting point for our image. I've used anapsix/docker-oracle-java8 which is a lightweight image for Java 8 running on Ubuntu.
  • Line 4 - RUN instruction tells Docker to run a command, in this case apt-get update -y to update the apt package list in preparation for the next step.  
  • Line 5 - tells Docker to run apt-get install -y to download and install maven on the image. The image will use Maven to build our app.
  • Line 8 - WORKDIR command tells Docker to create a working directory on the image. This directory will be used by the ADD and RUN commands that are defined below.
  • Line 10 - ADD command tells Docker to add the application POM from the host machine, to the app directory on the image.
  • Line 11 - tells Docker to add the application source from the host machine to /app/src on the image. At this point the image has everything it need to build the project.
  • Line 15 - tells Docker to run the mvn package -DskipTests=true command from the /app directory on the image. Maven will download all required dependencies and build an executable jar in the /app/target directory.  
  • Line 18 - ENTRYPOINT tells Docker what command to run when the container is started. The comma separated list of values consists of an executable (java in our instance) and a number of parameters. The entry point defined here tells Docker to run the executable JAR from the /app/target directory.       



Creating the Image 

Now that we've defined a Dockerfile lets put it to work by building an image. Run the docker build command specifying an image name and the location of the Dockerfile. For example docker build -t "docker-sample-1" .

 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
brianh@brianh-VirtualBox:~/apps/docker-spring-boot/docker-sample-1$ docker build -t "docker-sample-1" .
Sending build context to Docker daemon 12.83 MB
Step 1 : FROM anapsix/docker-oracle-java8
 ---> a8a9dcb0ac64
Step 2 : RUN apt-get update -y
 ---> Running in 5d477ccb8f46
Ign http://archive.ubuntu.com trusty InRelease
Get:1 http://ppa.launchpad.net trusty InRelease [15.5 kB]
Get:2 http://archive.ubuntu.com trusty-updates InRelease [65.9 kB]
Get:3 http://archive.ubuntu.com trusty-security InRelease [65.9 kB]
Hit http://archive.ubuntu.com trusty Release.gpg
Hit http://archive.ubuntu.com trusty Release

// Lots of output from apt update and maven build removed for brevity

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 9:06:42.103s
[INFO] Finished at: Thu Jul 21 16:24:04 UTC 2016
[INFO] Final Memory: 21M/107M
[INFO] ------------------------------------------------------------------------
 ---> b362beb8e90d
Removing intermediate container 512fd61457e6
Step 8 : RUN ls /app/target
 ---> Running in 7eba00207f50
classes
docker-sample-1-0.1.0.jar
docker-sample-1-0.1.0.jar.original
generated-sources
maven-archiver
maven-status
 ---> 613a5f6ef5bb
Removing intermediate container 7eba00207f50
Step 9 : ENTRYPOINT java -jar /app/target/docker-sample-1-0.1.0.jar
 ---> Running in e43707589d45
 ---> 2e4f625b102e
Removing intermediate container e43707589d45
Successfully built 2e4f625b102e

Docker runs each instruction in the Dockerfile step by step. Step 1 in this instance runs quickly because I already have this image cached locally. When you're building this for the first time you likely wont have the anapsix/docker-oracle-java-8 image, so Docker will pull it from the Docker Hub repository. Subsequent builds will use the local cached image and as a result will run much quicker. For each step Docker does the following
  • creates a new intermediate container
  • runs the command inside that container 
  • commits the change as a new image layer 
  • removes the intermediate container and moves to the next step
The new image consists of multiple layers stacked one on top of the other, one for each instruction in the Dockerfile. Run the docker images command to see the newly created image.

1
2
3
4
brianh@brianh-VirtualBox:~/apps$ docker images
REPOSITORY                    TAG                 IMAGE ID            CREATED             SIZE
docker-sample-1               latest              2e4f625b102e        33 minutes ago      936 MB
anapsix/docker-oracle-java8   latest              a8a9dcb0ac64        3 weeks ago         784.5 MB
                                                              
Note the Image ID is the same as that output at the end of the build. To see the various layers that make up the new image run the docker history command as follows. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
brianh@brianh-VirtualBox:~/apps$ docker history docker-sample-1
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
2e4f625b102e        37 minutes ago      /bin/sh -c #(nop) ENTRYPOINT ["java" "-jar" "   0 B                 
613a5f6ef5bb        37 minutes ago      /bin/sh -c ls /app/target                       0 B                 
b362beb8e90d        37 minutes ago      /bin/sh -c mvn package -DskipTests=true         37.51 MB            
0f3103ac18be        9 hours ago         /bin/sh -c #(nop) ADD dir:82830cfed5011783b44   1.276 kB            
73ccd6348460        9 hours ago         /bin/sh -c #(nop) ADD file:cbad7ca7f8efa76f28   1.349 kB            
22f2ab199dd7        9 hours ago         /bin/sh -c #(nop) WORKDIR /app                  0 B                 
5e3e8435f2b4        9 hours ago         /bin/sh -c apt-get install -y maven             92.07 MB            
6c352c184d38        9 hours ago         /bin/sh -c apt-get update -y                    21.9 MB             
a8a9dcb0ac64        3 weeks ago         /bin/sh -c #(nop) ENV JAVA_HOME=/usr/lib/jvm/   0 B                 
<missing>           3 weeks ago         /bin/sh -c apt-get update && DEBIAN_FRONTEND=   583.6 MB            
<missing>           3 weeks ago         /bin/sh -c apt-key adv --keyserver keyserver.   25.18 kB            
<missing>           3 weeks ago         /bin/sh -c echo "deb http://ppa.launchpad.net   65 B                
<missing>           3 weeks ago         /bin/sh -c echo "oracle-java8-installer share   2.677 MB            
<missing>           3 weeks ago         /bin/sh -c #(nop) ENV LC_ALL=en_US.UTF-8        0 B                 
<missing>           3 weeks ago         /bin/sh -c #(nop) ENV LANG=en_US.UTF-8          0 B                 
<missing>           3 weeks ago         /bin/sh -c locale-gen en_US.UTF-8               1.621 MB            
<missing>           3 weeks ago         /bin/sh -c #(nop) MAINTAINER Anastas Dancha "   0 B                 
<missing>           3 weeks ago         /bin/sh -c #(nop) CMD ["/bin/bash"]             0 B                 
<missing>           3 weeks ago         /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/   1.895 kB            
<missing>           3 weeks ago         /bin/sh -c rm -rf /var/lib/apt/lists/*          0 B                 
<missing>           3 weeks ago         /bin/sh -c set -xe   && echo '#!/bin/sh' > /u   8.841 MB 
   
Note that lines 3 to 11 list the image layers that were added as a result of each instruction executed in our Dockerfile.

Running the Container 

Now that we've built the image we're ready to start a container using the docker run command. When the container starts it will launch the Java app on container port 8080. We need to tell the Docker container to map port 8080 to a port on the host machine so that we can access the application running inside the container. We do this using the -p 8080:8080 argument as part of the docker run command.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
brianh@brianh-VirtualBox:~/apps/docker-spring-boot/docker-sample-1$ docker run -p 8080:8080 docker-sample-1

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.2.7.RELEASE)

3051 [main] INFO  com.blog.samples.boot.Application - Starting Application v0.1.0 on 81828366ca4e with PID 1 (/app/target/docker-sample-1-0.1.0.jar started by root in /app) 
3382 [main] INFO  o.s.b.c.e.AnnotationConfigEmbeddedWebApplicationContext - Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@66429cee: startup date [Thu Jul 21 17:15:31 UTC 2016]; root of context hierarchy 
6707 [main] INFO  o.s.b.f.s.DefaultListableBeanFactory - Overriding bean definition for bean 'beanNameViewResolver': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]] 
8176 [main] INFO  o.h.validator.internal.util.Version - HV000001: Hibernate Validator 5.1.3.Final 
10328 [main] INFO  o.s.b.c.e.t.TomcatEmbeddedServletContainer - Tomcat initialized with port(s): 8080 (http) 
11235 [main] INFO  o.a.catalina.core.StandardService - Starting service Tomcat 

When the container starts it runs the ENTRYPOINT command java -jar /app/target/docker-sample-1-0.1.o.jar specified in the Dockerfile,  The application will start on container port 8080 and Docker will bind to port 8080 on the host.

Testing the Application

To test that the app is up and running we can call the app health check using a simple curl command. We should see some activity in the logs and receive a HTTP 200 response.

1
2
3
4
5
6
7
brianh@brianh-VirtualBox:~/apps/docker-spring-boot/docker-sample-1$ curl -i localhost:8080/health
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Application-Context: application
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 21 Jul 2016 17:15:54 GMT



Other Examples  

Next we'll look at 2 more Docker examples that are slight variations of the one above. I won't describe these in the same detail but feel free to pull them from Github and have a play around.

Example two is a simplified version of the first example, and simply adds the app JAR to the container and runs the app. In this instance you build and package the app on the host and simply copy the JAR into the container. This keeps the image slightly lighter as it doesn't have to create a maven repository like the first example did.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
FROM anapsix/docker-oracle-java8

# Creating working directory
WORKDIR /app

# Add src to working directory
ADD target/docker-smaple-2-0.1.0.jar /app/docker-sample-2-0.1.0.jar

# Start app
ENTRYPOINT ["java","-jar","/app/docker-sample-2-0.1.0.jar"]

Example 3 is a slightly different variation again. Rather than using the ADD command to copy the application artifact from the host machine, we use pass a URL to the ADD command to pull the artifact from a repository. I've used S3 in this example but you could pull your app from a CI server like Team City, Jenkins or anywhere else you please.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
FROM anapsix/docker-oracle-java8

# Creating working directory
WORKDIR /app

# Pull artifact from repo and add to working directory
ADD https://s3-us-west-2.amazonaws.com/docker-boot-artifact/docker-sample-3-0.1.0.jar /app/docker-sample-3-0.1.0.jar

# Start app
ENTRYPOINT ["java","-jar","/app/docker-sample-3-0.1.0.jar"]



Source Code  

The source code for each of these examples is on Github and split into 3 separate projects. Pull the code down, play around with it and if you have any comments, questions or suggestions just leave a note below.