Apache Camel – developing application from the scratch (part 2 / 2)

This is the second part of the tutorial where we are creating an invoices processing application using Apache Camel. In case you missed it, be sure to look at the first part. Previously we’ve defined functional requirements for the system, created gateway, splitter, filter and content-based router component. Let’s continue with creating a transformer.

5. Transforming invoices to the payments

We’ve successfully filtered out “too expensive” invoices from the system now (they might need manual inspection or so). The important thing is that we can now take an invoice and generate payment from it. First, let’s add Payment class to the banking package:

package com.vrtoonjava.banking;

import com.google.common.base.Objects;

import java.math.BigDecimal;

public class Payment {

    private final String senderAccount;
    private final String receiverAccount;
    private final BigDecimal dollars;

    public Payment(String senderAccount, String receiverAccount, BigDecimal dollars) {
        this.senderAccount = senderAccount;
        this.receiverAccount = receiverAccount;
        this.dollars = dollars;
    }

    public String getSenderAccount() {
        return senderAccount;
    }

    public String getReceiverAccount() {
        return receiverAccount;
    }

    public BigDecimal getDollars() {
        return dollars;
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this)
                .add("senderAccount", senderAccount)
                .add("receiverAccount", receiverAccount)
                .add("dollars", dollars)
                .toString();
    }

}

Because we will have two ways how to create a payment (from local and foreign invoices), let’s define a common contract (interface) for creating payments. Put interface PaymentCreator to the banking package:

package com.vrtoonjava.banking;

import com.vrtoonjava.invoices.Invoice;

/**
 * Creates payment for bank from the invoice.
 * Real world implementation might do some I/O expensive stuff.
 */
public interface PaymentCreator {

    Payment createPayment(Invoice invoice) throws PaymentException;

}

Technically, this is a simple parametrized Factory. Note that it throws PaymentException. We’ll get to the exception handling later, but here’s the code for the simple PaymentException:

package com.vrtoonjava.banking;

public class PaymentException extends Exception {

    public PaymentException(String message) {
        super(message);
    }

}

Now we’re good to add two implementations to the invoices package. First, let’s create LocalPaymentCreator class:

package com.vrtoonjava.invoices;

import com.vrtoonjava.banking.Payment;
import com.vrtoonjava.banking.PaymentCreator;
import com.vrtoonjava.banking.PaymentException;
import org.springframework.stereotype.Component;

@Component
public class LocalPaymentCreator implements PaymentCreator {

    // hard coded account value for demo purposes
    private static final String CURRENT_LOCAL_ACC = "current-local-acc";

    @Override
    public Payment createPayment(Invoice invoice) throws PaymentException {
        if (null == invoice.getAccount()) {
            throw new PaymentException("Account can not be empty when creating local payment!");
        }

        return new Payment(CURRENT_LOCAL_ACC, invoice.getAccount(), invoice.getDollars());
    }

}

Another creator will be ForeignPaymentCreator with rather straightforward implementation:

package com.vrtoonjava.invoices;

import com.vrtoonjava.banking.Payment;
import com.vrtoonjava.banking.PaymentCreator;
import com.vrtoonjava.banking.PaymentException;
import org.springframework.stereotype.Component;

@Component
public class ForeignPaymentCreator implements PaymentCreator {

    // hard coded account value for demo purposes
    private static final String CURRENT_IBAN_ACC = "current-iban-acc";

    @Override
    public Payment createPayment(Invoice invoice) throws PaymentException {
        if (null == invoice.getIban()) {
            throw new PaymentException("IBAN mustn't be null when creating foreign payment!");
        }

        return new Payment(CURRENT_IBAN_ACC, invoice.getIban(), invoice.getDollars());
    }

}

Those two creators are simple Spring beans, and Apache Camel provides a really nice way of connecting them to the route. We will be creating two transformers by using transform() method on Camel’s Java DSL. We will plug correct transformers to both seda:foreignInvoicesChannel and seda:localInvoicesChannel and make them to forward results to seda:bankingChannel. Add following code to your configure method:

 
from("seda:foreignInvoicesChannel")
        .transform().method("foreignPaymentCreator", "createPayment")
        .to("seda:bankingChannel");

from("seda:localInvoicesChannel")
        .transform().method("localPaymentCreator", "createPayment")
        .to("seda:bankingChannel");

6. Passing payments to the banking service (Service Activator)

Payments are ready and messages containing them are waiting in the seda:bankingChannel. The last step of the flow is to use Service Activator component. The way it works is simple – when a new message appears in a channel, Apache Camel invokes logic specified in a Service Activator component. In other words, we’re connecting external service to our existing messaging infrastructure.
In order to do that we first need to see a contract for the banking service. So put interface BankingService to the banking package (in the real world this would probably reside in some external module):

package com.vrtoonjava.banking;

/**
 * Contract for communication with bank.
 */
public interface BankingService {

    void pay(Payment payment) throws PaymentException;

}

Now we will need an actual implementation of the BankingService. Again, it’s highly unlikely that implementation would reside in our project (it would probably be remotely exposed service), but let’s at least create some mock implementation for the tutorial purposes. Add MockBankingService class to the banking package:

package com.vrtoonjava.banking;

import org.springframework.stereotype.Service;

import java.util.Random;

/**
 * Mock service that simulates some banking behavior.
 * In real world, we might use some web service or a proxy of real service.
 */
@Service
public class MockBankingService implements BankingService {

    private final Random rand = new Random();

    @Override
    public void pay(Payment payment) throws PaymentException {
        if (rand.nextDouble() > 0.9) {
            throw new PaymentException("Banking services are offline, try again later!");
        }

        System.out.println("Processing payment " + payment);
    }

}

Mock implementation creates on some random occasions (~10%) a failure. Of course for the better decoupling we’re not going to use it directly, we will create dependency from our custom component on a contract (interface) instead. Let’s add PaymentProcessor class to the invoices package now:

package com.vrtoonjava.invoices;

import com.vrtoonjava.banking.BankingService;
import com.vrtoonjava.banking.Payment;
import com.vrtoonjava.banking.PaymentException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Endpoint that picks Payments from the system and dispatches them to the
 * service provided by bank.
 */
@Component
public class PaymentProcessor {

    @Autowired
    BankingService bankingService;

    public void processPayment(Payment payment) throws PaymentException {
        bankingService.pay(payment);
    }

}

Apache Camel provides easy way how to invoke method on arbitrary bean when a message arrives to certain endpoint (EIP describes this as Service Activator) by using bean() method on Camel’s Java DSL:

from("seda:bankingChannel")
        .bean(PaymentProcessor.class, "processPayment");

Error handling

One of the biggest challenges of messaging systems is to properly identify and handle error situations. EAI describes plenty of approaches and we will use Camel’s implementation of Dead Letter Channel EIP. Dead Letter Channel is just another channel and we can take proper action when an error message appears in this channel. In the real world applications we would probably go for some retry logic or professional reporting, in our sample tutorial we will just print out the cause of the error. Let’s modify previously defined Service Activator and plug in errorHandler() component. When PaymentProcessor throws an exception, this errorHandler will forward original message that caused an error to Dead Letter Channel:

from("seda:bankingChannel")
        .errorHandler(deadLetterChannel("log:failedPayments"))
        .bean(PaymentProcessor.class, "processPayment");

Finally, here is the final and complete route:

package com.vrtoonjava.routes;

import com.vrtoonjava.invoices.LowEnoughAmountPredicate;
import com.vrtoonjava.invoices.PaymentProcessor;
import org.apache.camel.LoggingLevel;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;

@Component
public class InvoicesRouteBuilder extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("seda:newInvoicesChannel")
                .log(LoggingLevel.INFO, "Invoices processing STARTED")
                .split(body())
                .to("seda:singleInvoicesChannel");

        from("seda:singleInvoicesChannel")
                .filter(new LowEnoughAmountPredicate())
                .to("seda:filteredInvoicesChannel");

        from("seda:filteredInvoicesChannel")
                .choice()
                    .when().simple("${body.isForeign}")
                        .to("seda:foreignInvoicesChannel")
                    .otherwise()
                        .to("seda:localInvoicesChannel");

        from("seda:foreignInvoicesChannel")
                .transform().method("foreignPaymentCreator", "createPayment")
                .to("seda:bankingChannel");

        from("seda:localInvoicesChannel")
                .transform().method("localPaymentCreator", "createPayment")
                .to("seda:bankingChannel");

        from("seda:bankingChannel")
                .errorHandler(deadLetterChannel("log:failedPayments"))
                .bean(PaymentProcessor.class, "processPayment");
    }

}

Running the whole thing

We’ll create a job now that will (at fixed rate) send new invoices to the system. It is only a standard Spring bean that utilizes Spring’s @Scheduled annotation. So let’s add a new class – InvoicesJob to the project:

package com.vrtoonjava.invoices;

import org.apache.camel.Produce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Component
public class InvoicesJob {

    private int limit = 10; // default value, configurable

    @Autowired
    InvoiceCollectorGateway invoiceCollector;

    @Autowired
    InvoiceGenerator invoiceGenerator;

    @Scheduled(fixedRate = 4000)
    public void scheduleInvoicesHandling() {
        Collection<Invoice> invoices = generateInvoices(limit);
        System.out.println("\n===========> Sending " + invoices.size() + " invoices to the system");
        invoiceCollector.collectInvoices(invoices);
    }

    // configurable from Injector
    public void setLimit(int limit) {
        this.limit = limit;
    }

    private Collection<Invoice> generateInvoices(int limit) {
        List<Invoice> invoices = new ArrayList<>();
        for (int i = 0; i < limit; i++) {
            invoices.add(invoiceGenerator.nextInvoice());
        }

        return invoices;
    }
}

Job invokes (every 4 seconds) InvoicesGenerator and forwards invoices to the Gateway (first component we read about). To make it work we also need InvoicesGenerator class:

package com.vrtoonjava.invoices;

import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Random;

/**
 * Utility class for generating invoices.
 */
@Component
public class InvoiceGenerator {

    private Random rand = new Random();

    public Invoice nextInvoice() {
        return new Invoice(rand.nextBoolean() ? iban() : null, address(), account(), dollars());
    }

    private BigDecimal dollars() {
        return new BigDecimal(1 + rand.nextInt(20_000));
    }

    private String account() {
        return "test-account " + rand.nextInt(1000) + 1000;
    }

    private String address() {
        return "Test Street " + rand.nextInt(100) + 1;
    }

    private String iban() {
        return "test-iban-" + rand.nextInt(1000) + 1000;
    }

}

This is only a simple mock facility that’ll allow us to see the system working. In the real world we wouldn’t use any generator but probably some exposed service instead.

Now under resources folder create a new spring config file – invoices-context.xml and declare component scanning and task scheduling support:

<?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:task = "http://www.springframework.org/schema/task"
       xmlns:context = "http://www.springframework.org/schema/context"
       xsi:schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <import resource = "camel-config.xml" />

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

    <task:executor id = "executor" pool-size="10" />
    <task:scheduler id = "scheduler" pool-size="10" />
    <task:annotation-driven executor="executor" scheduler="scheduler" />

</beans>

To see the whole thing running we need one more last piece – standard Java main application where we will create Spring’s ApplicationContext.

package com.vrtoonjava.invoices;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Entry point of the application.
 * Creates Spring context, lets Spring to schedule job and use schema.
 */
public class InvoicesApplication {

    public static void main(String[] args) {
        new ClassPathXmlApplicationContext("/invoices-context.xml");
    }

}

Simply run mvn clean install from command line and launch the main method in InvoicesApplication class. You should be able to see similar output:

===========> Sending 10 invoices to the system
13:48:54.347 INFO  [Camel (camel-1) thread #0 - seda://newInvoicesChannel][route1] Invoices processing STARTED
Amount of $4201 can be automatically processed by system
Amount of $15110 can not be automatically processed by system
Amount of $17165 can not be automatically processed by system
Amount of $1193 can be automatically processed by system
Amount of $6077 can be automatically processed by system
Amount of $17164 can not be automatically processed by system
Amount of $11272 can not be automatically processed by system
Processing payment Payment{senderAccount=current-local-acc, receiverAccount=test-account 1901000, dollars=4201}
Amount of $3598 can be automatically processed by system
Amount of $14449 can not be automatically processed by system
Processing payment Payment{senderAccount=current-local-acc, receiverAccount=test-account 8911000, dollars=1193}
Amount of $12486 can not be automatically processed by system
13:48:54.365 INFO  [Camel (camel-1) thread #5 - seda://bankingChannel][failedPayments] Exchange[ExchangePattern: InOnly, BodyType: com.vrtoonjava.banking.Payment, Body: Payment{senderAccount=current-iban-acc, receiverAccount=test-iban-7451000, dollars=6077}]
Processing payment Payment{senderAccount=current-iban-acc, receiverAccount=test-iban-6201000, dollars=3598}

Apache Camel – developing application from the scratch (part 1 / 2)

Before we start

Some time ago I wrote a tutorial on Spring Integration to demonstrate how to use Spring Integration in sample application inspired by real-world invoices processing system. I got quite positive feedback on that one so I decided that I will show you how can we build very same application using Apache Camel – the greatest competitor of Spring Integration.

The application that we are about to build will be pretty much the same and so will be lot of the text in this tutorial. The new parts will mostly be focusing on the Apache Camel and its usage.

You can either follow this tutorial step-by-step and create application from the scratch yourself, or you can go ahead and get the code from github:
DOWNLOAD SOURCES HERE: https://github.com/vrto/apache-camel-invoices

Please note that there are more ways how to write Camel “routes” – Java DSL, Spring XML, Scala DSL … In this tutorial I will be using Java DSL, but for those interested – you can also find same project configured via Spring XML here.
Whichever way you prefer, it’s time to get started!

Application for processing invoices – functional description

Imagine that you’re working for some company that periodically receives a large amount of invoices from various contractors. We are about to build a system that will be able to receive invoices, filter out relevant ones, create payments (either local or foreign) and send them to some banking service. Even though the system will be rather naive and certainly not enterprise-ready, we will try to build it with good scalability, flexibility and decoupled design in the mind.

Apache Camel is an integration framework – it means that it provides meaningful abstractions for the complex systems you’re integrating.
There are plenty of important concepts to understand for this tutorial. Let me sum them up for you:

  • Camel Context: Runtime system that keeps all pieces together.
  • Message: Fundamental entity – core principle of messaging. It consists of Headers, Body and Attachments.
  • Exchange: Container for messages (abstraction of what is actually sent over the system). It contains In and optionally also Out message.
  • Route: Chain of processors. If you like more “academic” explanation then route is a graph, where “node” is represented by some Processor and “line” is represented by some Channel.
  • Processor: Uses/modifies incoming exchange. Output of one processor is connected to the input of another one.
  • Endpoint: Models end of channel. Configured using URI. Example:
    file:data/inbox?delay=5000
  • Component: Factory for endpoints. Referred with prefixes (jms:, file:, etc.).

If you want to know more details about core camel concepts I highly encourage you to read very good book Camel In Action.

Now that we have some understanding of basic concepts, let’s take a look at the following picture which is a summary of the system, and walk over important pieces:

Invoices Integration Schema

On the picture you can see a route that illustrates our messaging structure and core components of the system – they are marked with red numbers. Let’s walk over those (we will get back to each component in more detail later):

  1. Invoices Gateway – this is the place where we will put new invoices so they can enter the messaging layer.
  2. Splitter – the system is designed to accept a collection of invoices, but we will need to process each invoice individually. More specifically, message with body of Collection type will be split to the multiple messages, where each message will have individual invoice as a body.
  3. Filter – Our system is designed to automatically process only those invoices that issue less than $10,000.
  4. Content-based router – Some invoices use IBAN account numbers and we have two different accounts – one for the local transactions and one for the foreign transactions. The job of a router component is to send a message carrying invoice to the correct channel – either for local invoices, or for the foreign invoices.
  5. Transformers – While we accept Invoices in to the system, our banking APIs work with other types – Payments. Job of the transformer component is to take some message and transform it to another message according to provided logic. We want to transform the payload of original message (invoice) to the new payload – payment.
  6. Banking Service Activator – After we have processed invoices and generated some actual payments we’re ready to talk to the external banking system. We have exposed service of such systems and when message carrying payment enters the correct (banking) channel, we want to activate some logic – passing the payment to the bank and let the bank do further processing.

Creating the project

By now you should have a high level overview of what the system does and how is it structured. Before we start coding you will need an actual Maven project and set up the structure and required dependencies. If you’re familiar with Maven then see pom.xml file below, else if you want to save some time you’re welcome to use a project template I’ve created for you: download the Maven project template.

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>apache-camel-invoices</groupId>
    <artifactId>apache-camel-invoices</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <camel-version>2.12.0</camel-version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-core</artifactId>
            <version>${camel-version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-spring</artifactId>
            <version>${camel-version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-quartz</artifactId>
            <version>2.9.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-test-spring</artifactId>
            <version>${camel-version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>14.0.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
			<!--
			
			- uncomment when InvoicesApplication class will exist
			
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>com.vrtoonjava.invoices.InvoicesApplication</mainClass>
                </configuration>
            </plugin>
			-->
        </plugins>
    </build>

</project>

Bootstraping Camel

Before using Apache Camel, we need to bootstrap it a little. First, we will create package com.vrtoonjava.routes where we will put our route configurations. Second, we will create class InvoicesRouteBuilder which will be the place for configuring our routes. So create class just like this:

package com.vrtoonjava.routes;

import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;

@Component
public class InvoicesRouteBuilder extends RouteBuilder {

    @Override
    public void configure() throws Exception {
		//TODO configure route
	}
}

Last thing to do is plug Camel to Spring (Camel will be using Spring as a registry for beans). Add file camel-config.xml to your src/main/resources folder with following contents:

<?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:camel="http://camel.apache.org/schema/spring"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
         http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <routeBuilder ref="invoicesRouteBuilder"/>
    </camelContext>

</beans>

Let’s now walk over the six major components of the system in more details and get hands on the actual code.

1. Invoices Gateway

First, let’s see the code for Invoice – which will be one of the core classes in our system. I will be using package com.vrtoonjava as root package, and invoices and banking as sub-packages:

package com.vrtoonjava.invoices;

import com.google.common.base.Objects;

import java.math.BigDecimal;

public class Invoice {

    private final String iban;
    private final String address;
    private final String account;
    private final BigDecimal dollars;

    public Invoice(String iban, String address, String account, BigDecimal dollars) {
        this.iban = iban;
        this.address = address;
        this.account = account;
        this.dollars = dollars;
    }

    public boolean isForeign() {
        return null != iban && !iban.isEmpty();
    }

    public String getAddress() {
        return address;
    }

    public String getAccount() {
        return account;
    }

    public BigDecimal getDollars() {
        return dollars;
    }

    public String getIban() {
        return iban;
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this)
                .add("iban", iban)
                .add("address", address)
                .add("account", account)
                .add("dollars", dollars)
                .toString();
    }

}

Imagine that we’re getting invoices from an another system (be it database, web-service or something else), but we don’t want to couple this part to the integration layer. We will use Gateway component for that purpose. Gateway introduces a contract that decouples client code from the integration layer (Apache Camel dependencies in our case). Let’s see the code for InvoiceCollectorGateway:

package com.vrtoonjava.invoices;

import java.util.Collection;

/**
 * Defines a contract that decouples client from the Apache Camel framework.
 */
public interface InvoiceCollectorGateway {

    void collectInvoices(Collection<Invoice> invoices);

}

We have defined this interface in order for client to depend only on the contract. Actual implementation will be Camel-specific and we can create it like this:

package com.vrtoonjava.invoices;

import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.springframework.stereotype.Component;

import java.util.Collection;

@Component
public class CamelInvoiceCollectorGateway implements InvoiceCollectorGateway {

    @Produce(uri = "seda:newInvoicesChannel")
    ProducerTemplate producerTemplate;

    @Override
    public void collectInvoices(Collection<Invoice> invoices) {
        producerTemplate.sendBody(invoices);
    }

}

Note the @Produce annotation. This annotation tells Camel that field producerTemplate is a producer to endpoint seda:newInvoicesChannel. When client calls collectInvoices method, gateway will send a new message (containing List body) to the seda:newInvoicesChannel channel. That leaves client decoupled from the messaging facilities, but lets us place the result to the real messaging channel.

Why SEDA?

Usually, when building messaging systems in Java, you end up using something like JMS that provides lot of useful features, like high reliability or message persistence. In this tutorial we will be using lighter alternative based on SEDA component – it allows us to utilize asynchronous in-memory messaging with pretty much zero configuration.

2. Invoices Splitter

From the Gateway we’re sending one big message to the system that contains a collection of invoices – in other words – Message has body of Collection type. As we want to process invoices individually, we will get the result from the seda:newInvoicesChannel and use a splitter component, that will create multiple messages. Each of these new messages will have a body of Invoice type. We will then place messages to the new channel – seda:singleInvoicesChannel. This is how we define splitter (add following code to the configure method of your InvoicesRouteBuilder):

from("seda:newInvoicesChannel")
        .log(LoggingLevel.INFO, "Invoices processing STARTED")
        .split(body())
        .to("seda:singleInvoicesChannel");

3. Filtering some invoices

A business use case of our system requires us to automatically process only those invoices that that issue us less than $10,000. For this purpose we will introduce a Filter component. We will grab messages from the seda:singleInvoicesChannel, apply our filtering logic on them, and then write matched results to the new seda:filteredInvoicesChannel channel. In Apache Camel you can plug in your own predicates that contain filtering logic. First, let’s define such predicate (by extending Camel’s org.apache.camel.Predicate):

package com.vrtoonjava.invoices;

import org.apache.camel.Exchange;
import org.apache.camel.Predicate;

public class LowEnoughAmountPredicate implements Predicate {

    public static final int LOW_ENOUGH_THRESHOLD = 10_000;

    @Override
    public boolean matches(Exchange exchange) {
        Invoice invoice = exchange.getIn().getBody(Invoice.class);
        boolean lowEnough = invoice.getDollars().intValue() < LOW_ENOUGH_THRESHOLD;
        System.out.println("Amount of $" + invoice.getDollars()
                + (lowEnough ? " can" : " can not") + " be automatically processed by system");

        return lowEnough;
    }

}

For the sake of brevity, I am not pasting unit tests in this tutorial – but if you’re interested go ahead and download github project and see the tests for yourself.

Now we need to connect this predicate to our route, so add following code to your configure method:

from("seda:singleInvoicesChannel")
        .filter(new LowEnoughAmountPredicate())
        .to("seda:filteredInvoicesChannel");

4. Routing invoices

So far, we’ve splitted and filtered out some invoices. Now it’s time to inspect contents of the each invoice more closely and decide, whether it is an invoice issued from the current country (local), or from an another country (foreign). Apache Camel allows use to define Content-Based Router by using choice() method in Java DSL. We can even directly access body of the message in Java DSL and perform some simple evaluations. Add following code to your configure method (and note how we are accessing body by using ${body.isForeign} standard expression):

from("seda:filteredInvoicesChannel")
        .choice()
            .when().simple("${body.isForeign}")
                .to("seda:foreignInvoicesChannel")
            .otherwise()
                .to("seda:localInvoicesChannel");

We will continue developing this application in the second part of this tutorial.

Spring Integration – developing application from the scratch (part 2 / 2)

This is the second part of the tutorial where we are creating an invoices processing application using Spring Integration. In case you missed it, be sure to look at the first part. Previously we’ve defined functional requirements for the system, created gateway, splitter, filter and router component. Let’s continue with creating a transformer.

5. Transforming invoices to the payments

We’ve successfully filtered out “too expensive” invoices from the system now (they might need manual inspection or so). The important thing is that we can now take an invoice and generate payment from it. First, let’s add Payment class to the banking package:

package com.vrtoonjava.banking;

import com.google.common.base.Objects;

import java.math.BigDecimal;

public class Payment {

    private final String senderAccount;
    private final String receiverAccount;
    private final BigDecimal dollars;

    public Payment(String senderAccount, String receiverAccount, BigDecimal dollars) {
        this.senderAccount = senderAccount;
        this.receiverAccount = receiverAccount;
        this.dollars = dollars;
    }

    public String getSenderAccount() {
        return senderAccount;
    }

    public String getReceiverAccount() {
        return receiverAccount;
    }

    public BigDecimal getDollars() {
        return dollars;
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this)
                .add("senderAccount", senderAccount)
                .add("receiverAccount", receiverAccount)
                .add("dollars", dollars)
                .toString();
    }

}

Because we will have two ways how to create a payment (from local and foreign invoices), let’s define a common contract (interface) for creating payments. Put interface PaymentCreator to the banking package:

package com.vrtoonjava.banking;

import com.vrtoonjava.invoices.Invoice;

/**
 * Creates payment for bank from the invoice.
 * Real world implementation might do some I/O expensive stuff.
 */
public interface PaymentCreator {

    Payment createPayment(Invoice invoice) throws PaymentException;

}

Technically, this is a simple parametrized Factory. Note that it throws PaymentException. We’ll get to the exception handling later, but here’s the code for the simple PaymentException:

package com.vrtoonjava.banking;

public class PaymentException extends Exception {

    public PaymentException(String message) {
        super(message);
    }

}

Now we’re good to add two implementations to the invoices package. First, let’s create LocalPaymentCreator class:

package com.vrtoonjava.invoices;

import com.vrtoonjava.banking.Payment;
import com.vrtoonjava.banking.PaymentCreator;
import com.vrtoonjava.banking.PaymentException;
import org.springframework.integration.annotation.Transformer;
import org.springframework.stereotype.Component;

@Component
public class LocalPaymentCreator implements PaymentCreator {

    // hard coded account value for demo purposes
    private static final String CURRENT_LOCAL_ACC = "current-local-acc";

    @Override
    @Transformer
    public Payment createPayment(Invoice invoice) throws PaymentException {
        if (null == invoice.getAccount()) {
            throw new PaymentException("Account can not be empty when creating local payment!");
        }

        return new Payment(CURRENT_LOCAL_ACC, invoice.getAccount(), invoice.getDollars());
    }

}

Another creator will be ForeignPaymentCreator with rather straightforward implementation:

package com.vrtoonjava.invoices;

import com.vrtoonjava.banking.Payment;
import com.vrtoonjava.banking.PaymentCreator;
import com.vrtoonjava.banking.PaymentException;
import org.springframework.integration.annotation.Transformer;
import org.springframework.stereotype.Component;

@Component
public class ForeignPaymentCreator implements PaymentCreator {

    // hard coded account value for demo purposes
    private static final String CURRENT_IBAN_ACC = "current-iban-acc";

    @Override
    @Transformer
    public Payment createPayment(Invoice invoice) throws PaymentException {
        if (null == invoice.getIban()) {
            throw new PaymentException("IBAN mustn't be null when creating foreign payment!");
        }

        return new Payment(CURRENT_IBAN_ACC, invoice.getIban(), invoice.getDollars());
    }

}

Interesting part about creators is @Transformer annotation. It’s a similar concept as we’ve used with @Filter annotation – only this time we’re telling to Spring Integration that it should use this method for payload transforming logic. Either way we will use foreign or local transformer, so new message will end in bankingChannel channel. Let’s define these new transformers in our schema file:

 
<int:transformer
    input-channel="localTransactions"
    output-channel="bankingChannel"
    ref="localPaymentCreator" />

<int:transformer
    input-channel="foreignTransactions"
    output-channel="bankingChannel"
    ref="foreignPaymentCreator" />

<int:channel id = "bankingChannel">
    <int:queue capacity="1000" />
</int:channel>

6. Passing payments to the banking service (Service Activator)

Payments are ready and messages containing them are waiting in the bankingChannel. The last step of the flow is to use Service Activator component. The way it works is simple – when a new message appears in a channel, Spring Integration invokes logic specified in a Service Activator component. So when a new payment appears in the bankingChannel, we want to pass it to the banking service.
In order to do that we first need to see a contract for the banking service. So put interface BankingService to the banking package (in the real world this would probably reside in some external module):

package com.vrtoonjava.banking;

/**
 * Contract for communication with bank.
 */
public interface BankingService {

    void pay(Payment payment) throws PaymentException;

}

Now we will need an actual implementation of the BankingService. Again, it’s highly unlikely that implementation would reside in our project (it would probably be remotely exposed service), but let’s at least create some mock implementation for the tutorial purposes. Add MockBankingService class to the banking package:

package com.vrtoonjava.banking;

import org.springframework.stereotype.Service;

import java.util.Random;

/**
 * Mock service that simulates some banking behavior.
 * In real world, we might use some web service or a proxy of real service.
 */
@Service
public class MockBankingService implements BankingService {

    private final Random rand = new Random();

    @Override
    public void pay(Payment payment) throws PaymentException {
        if (rand.nextDouble() > 0.9) {
            throw new PaymentException("Banking services are offline, try again later!");
        }

        System.out.println("Processing payment " + payment);
    }

}

Mock implementation creates on some random occasions (~10%) a failure. Of course for the better decoupling we’re not going to use it directly, we will create dependency from our custom component on a contract (interface) instead. Let’s add PaymentProcessor class to the invoices package now:

package com.vrtoonjava.invoices;

import com.vrtoonjava.banking.BankingService;
import com.vrtoonjava.banking.Payment;
import com.vrtoonjava.banking.PaymentException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.stereotype.Component;

/**
 * Endpoint that picks Payments from the system and dispatches them to the
 * service provided by bank.
 */
@Component
public class PaymentProcessor {

    @Autowired
    BankingService bankingService;

    @ServiceActivator
    public void processPayment(Payment payment) throws PaymentException {
        bankingService.pay(payment);
    }

}

Again – note the @ServiceActivator annotation. That means that Spring Integration should invoke corresponding method when service activator component comes to the game. To use the service activator we need to add it to the integration schema:

<int:service-activator input-channel="bankingChannel" ref="paymentProcessor">
    <int:poller fixed-rate="500" error-channel="failedPaymentsChannel" />
</int:service-activator>

<int:channel id = "failedPaymentsChannel" />

Note that we’re defining fixed-rate attribute which means that activator will be invoked every half second (if there is some message present in the bankingChannel). We’re also defining error-channel attribute, but we’ll get there just in moment.

Error handling

One of the biggest challenges of messaging systems is to properly identify and handle error situations. Spring Integration provides a technique called “error channels”, where we can (obviously) send error messages from the system. Error channel is just another channel and we can take proper action when an error message appears in this channel. In the real world applications we would probably go for some retry logic or professional reporting, in our sample tutorial we will just print out the cause of the error. In the previous component (Service Activator) we’ve specified error-channel property to refer to the failedPaymentsChannel. When message arrives to this channel we will invoke another Service Activator and print out the error. Here’s the implementation of the FailedPaymentHandler Service Activator:

package com.vrtoonjava.invoices;

import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.stereotype.Component;

@Component
public class FailedPaymentHandler {

    @ServiceActivator
    public void handleFailedPayment(Exception e) {
        System.out.println("Payment failed: " + e);
        // now the system should do something reasonable, like retrying the payment
        // omitted for the tutorial purposes
    }

}

And let’s hook it to the integration schema as usual:

<int:service-activator
    input-channel="failedPaymentsChannel"
    ref="failedPaymentHandler" />

Running the whole thing

We’ll create a job now that will (at fixed rate) send new invoices to the system. It is only a standard Spring bean that utilizes Spring’s @Scheduled annotation. So let’s add a new class – InvoicesJob to the project:

package com.vrtoonjava.invoices;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Job that every n-seconds generates invoices and sends them to the system.
 * In real world this might be endpoint receiving invoices from another system.
 */
@Component
public class InvoicesJob {

    private int limit = 10; // default value, configurable

    @Autowired
    InvoiceCollectorGateway invoiceCollector;

    @Autowired
    InvoiceGenerator invoiceGenerator;

    @Scheduled(fixedRate = 4000)
    public void scheduleInvoicesHandling() {
        Collection<Invoice> invoices = generateInvoices(limit);
        System.out.println("\n===========> Sending " + invoices.size() + " invoices to the system");
        invoiceCollector.collectInvoices(invoices);
    }

    // configurable from Injector
    public void setLimit(int limit) {
        this.limit = limit;
    }

    private Collection<Invoice> generateInvoices(int limit) {
        List<Invoice> invoices = new ArrayList<>();
        for (int i = 0; i < limit; i++) {
            invoices.add(invoiceGenerator.nextInvoice());
        }

        return invoices;
    }

}

Job invokes (every 4 seconds) InvoicesGenerator and forwards invoices to the Gateway (first component we read about). To make it work we also need InvoicesGenerator class:

package com.vrtoonjava.invoices;

import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Random;

/**
 * Utility class for generating invoices.
 */
@Component
public class InvoiceGenerator {

    private Random rand = new Random();

    public Invoice nextInvoice() {
        return new Invoice(rand.nextBoolean() ? iban() : null, address(), account(), dollars());
    }

    private BigDecimal dollars() {
        return new BigDecimal(1 + rand.nextInt(20_000));
    }

    private String account() {
        return "test-account " + rand.nextInt(1000) + 1000;
    }

    private String address() {
        return "Test Street " + rand.nextInt(100) + 1;
    }

    private String iban() {
        return "test-iban-" + rand.nextInt(1000) + 1000;
    }

}

This is only a simple mock facility that’ll allow us to see the system working. In the real world we wouldn’t use any generator but probably some exposed service instead.

Now under resources folder create a new spring config file – invoices-context.xml and declare component scanning and task scheduling support:

<?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:task = "http://www.springframework.org/schema/task"
       xmlns:context = "http://www.springframework.org/schema/context"
       xsi:schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <import resource = "invoices-int-schema.xml" />

    <context:component-scan base-package = "com.vrtoonjava.invoices" />
    <context:component-scan base-package = "com.vrtoonjava.banking" />

    <task:executor id = "executor" pool-size="10" />
    <task:scheduler id = "scheduler" pool-size="10" />
    <task:annotation-driven executor="executor" scheduler="scheduler" />

</beans>

To see the whole thing running we need one more last piece – standard Java main application where we will create Spring’s ApplicationContext.

package com.vrtoonjava.invoices;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Entry point of the application.
 * Creates Spring context, lets Spring to schedule job and use schema.
 */
public class InvoicesApplication {

    public static void main(String[] args) {
        new ClassPathXmlApplicationContext("/invoices-context.xml");
    }

}

Simply run mvn clean install from command line and launch the main method in InvoicesApplication class. You should be able to see similar output:

===========> Sending 10 invoices to the system
Amount of $3441 can be automatically processed by system
Amount of $17419 can not be automatically processed by system
Processing payment Payment{senderAccount=current-local-acc, receiverAccount=test-account 1011000, dollars=3441}
Amount of $18442 can not be automatically processed by system
Amount of $19572 can not be automatically processed by system
Amount of $5471 can be automatically processed by system
Amount of $1663 can be automatically processed by system
Processing payment Payment{senderAccount=current-iban-acc, receiverAccount=test-iban-2211000, dollars=5471}
Amount of $13160 can not be automatically processed by system
Amount of $2213 can be automatically processed by system
Amount of $1423 can be automatically processed by system
Processing payment Payment{senderAccount=current-iban-acc, receiverAccount=test-iban-8051000, dollars=1663}
Amount of $1267 can be automatically processed by system
Payment failed: org.springframework.integration.MessageHandlingException: com.vrtoonjava.banking.PaymentException: Banking services are offline, try again later!
Processing payment Payment{senderAccount=current-iban-acc, receiverAccount=test-iban-6141000, dollars=1423}
Processing payment Payment{senderAccount=current-local-acc, receiverAccount=test-account 6761000, dollars=1267}

Spring Integration – developing application from the scratch (part 1 / 2)

Before we start

In this tutorial you will learn what is Spring Integration, how to use it and what kind of problems does it help to solve. We will build a sample application from the scratch and demonstrate some of the core components of Spring Integration. If you’re new to Spring check out another tutorial on Spring written by me – Shall we do some Spring together? Also note that you don’t need any special tooling, however you can get the best experience for building Spring Integration applications either with IntelliJ IDEA or Spring Tool Suite (you can get some fancy-looking diagrams with STS).

You can either follow this tutorial step-by-step and create application from the scratch yourself, or you can go ahead and get the code from github:
DOWNLOAD SOURCES HERE: https://github.com/vrto/spring-integration-invoices
Whichever way you prefer, it’s time to get started!

Application for processing invoices – functional description

Imagine that you’re working for some company that periodically receives a large amount of invoices from various contractors. We are about to build a system that will be able to receive invoices, filter out relevant ones, create payments (either local or foreign) and send them to some banking service. Even though the system will be rather naive and certainly not enterprise-ready, we will try to build it with good scalability, flexibility and decoupled design in the mind.

Before you go on, you must realize one thing: Spring Integration is (not only, but mostly) about messaging. Spring Integration is basically an embedded enterprise service bus that lets you seamlessly connect your business logic to the messaging channels. Messages can be handled both programmatically (via Spring Integration API) or automatically (by framework itself – higher level of decoupling). Message is the thing that travels across channels. Message has headers and payload – which will be in our case the actual relevant content (domain classes).

Let’s take a look at the following picture which is a summary of the system and walk over important pieces:

Invoices Integration Schema

On the picture you can see an integration diagram that illustrates our messaging structure and core components of the system – they are marked with red numbers. Let’s walk over those (we will get back to each component in more detail later):

  1. Invoices Gateway – this is the place where we will put new invoices so they can enter the messaging layer
  2. Splitter – the system is designed to accept a collection of invoices, but we will need to process each invoice individually. More specifically, message with payload of Collection type will be split to the multiple messages, where each message will have individual invoice as a payload.
  3. Filter – Our system is designed to automatically process only those invoices that issue less than $10,000
  4. Router – Some invoices use IBAN account numbers and we have two different accounts – one for the local transactions and one for the foreign transactions. The job of a router component is to send a message carrying invoice to the correct channel – either for local invoices, or for the foreign invoices.
  5. Transformers – While we accept Invoices in to the system, our banking APIs work with other types – Payments. Job of the transformer component is to take some message and transform it to another message according to provided logic. We want to transform the payload of original message (invoice) to the new payload – payment.
  6. Banking Service Activator – After we have processed invoices and generated some actual payments we’re ready to talk to the external banking system. We have exposed service of such systems and when message carrying payment enters the correct (banking) channel, we want to activate some logic – passing the payment to the bank and let the bank do further processing.

Creating the project

By now you should have a high level overview of what the system does and how is it structured. Before we start coding you will need an actual Maven project and set up the structure and required dependencies. If you’re familiar with Maven then see pom.xml file below, else if you want to save some time you’re welcome to use a project template I’ve created for you: download the Maven project template.

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>spring-integration-invoices</groupId>
    <artifactId>spring-integration-invoices</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-core</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>13.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.5.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Let’s now walk over the six major components of the system in more details and get hands on the actual code.

1. Invoices Gateway

First, let’s see the code for Invoice – which will be one of the core classes in our system. I will be using package com.vrtoonjava as root package, and invoices and banking as sub-packages:

package com.vrtoonjava.invoices;

import com.google.common.base.Objects;

import java.math.BigDecimal;

public class Invoice {

    private final String iban;
    private final String address;
    private final String account;
    private final BigDecimal dollars;

    public Invoice(String iban, String address, String account, BigDecimal dollars) {
        this.iban = iban;
        this.address = address;
        this.account = account;
        this.dollars = dollars;
    }

    public boolean isForeign() {
        return null != iban && !iban.isEmpty();
    }

    public String getAddress() {
        return address;
    }

    public String getAccount() {
        return account;
    }

    public BigDecimal getDollars() {
        return dollars;
    }

    public String getIban() {
        return iban;
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this)
                .add("iban", iban)
                .add("address", address)
                .add("account", account)
                .add("dollars", dollars)
                .toString();
    }

}

Imagine that we’re getting invoices from an another system (be it database, web-service or something else), but we don’t want to couple this part to the integration layer. We will use Gateway component for that purpose. Gateway introduces a contract that decouples client code from the integration layer (Spring Integration dependencies in our case). Let’s see the code for InvoiceCollectorGateway:

package com.vrtoonjava.invoices;

import java.util.Collection;

/**
 * Defines a contract that decouples client from the Spring Integration framework.
 */
public interface InvoiceCollectorGateway {

    void collectInvoices(Collection<Invoice> invoices);

}

Now, to actually use the Spring Integration we need to create a standard Spring configuration file and use Spring Integration namespace. To get started, here’s invoices-int-schema.xml file. Put it into src/main/resources. Note that we’ve already defined a logging-channel-adapter – which is a special channel where we will send messages from the logger. We’re also using wire-tap – you can think of it as sort of global interceptor that will send logging-related messages to the logger channel.

<?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:int = "http://www.springframework.org/schema/integration"
       xsi:schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">

    <!-- intercept and log every message -->
    <int:logging-channel-adapter id="logger" level="DEBUG" />
    <int:wire-tap channel = "logger" />
</beans>

Let’s get back to our gateway now. We’ve defined a gateway interface – that is the dependency that client will use. When client calls collectInvoices method, gateway will send a new message (containing List payload) to the newInvoicesChannel channel. That leaves client decoupled from the messaging facilities, but lets us place the result to the real messaging channel. To configure gateway, add following code to the integration schema config:

<int:channel id = "newInvoicesChannel" />

<int:gateway id="invoicesGateway"
     service-interface="com.vrtoonjava.invoices.InvoiceCollectorGateway">
    <int:method name="collectInvoices" request-channel="newInvoicesChannel" />
</int:gateway>

2. Invoices Splitter

From the Gateway we’re sending one big message to the system that contains a collection of invoices – in other words – Message has payload of Collection type. As we want to process invoices individually, we will get the result from the newInvoicesChannel and use a splitter component, that will create multiple messages. Each of these new messages will have a payload of Invoice type. We will then place messages to the new channel – singleInvoicesChannel. We will use a default Splitter that Spring Integration provides (by default Spring Integration uses DefaultMessageSplitter that does exactly what we want). This is how we define splitter:

<int:splitter
        input-channel="newInvoicesChannel"
        output-channel="singleInvoicesChannel" />

<int:channel id = "singleInvoicesChannel" />

3. Filtering some invoices

A business use case of our system requires us to automatically process only those invoices that that issue us less than $10,000. For this purpose we will introduce a Filter component. We will grab messages from the singleInvoicesChannel, apply our filtering logic on them, and then write matched results to the new filteredInvoicesChannel channel. First, let’s create a standard Java class that will contain filtering logic for single invoice. Note that we use @Component annotation (which makes it a standard Spring bean) and we annotate filtering method with @Filter annotation – that will tell Spring Integration to use this method for filtering logic:

package com.vrtoonjava.invoices;

import org.springframework.integration.annotation.Filter;
import org.springframework.stereotype.Component;

@Component
public class InvoiceFilter {

    public static final int LOW_ENOUGH_THRESHOLD = 10_000;

    @Filter
    public boolean accept(Invoice invoice) {
        boolean lowEnough = 
                invoice.getDollars().intValue() < LOW_ENOUGH_THRESHOLD;
        System.out.println("Amount of $" + invoice.getDollars()
                + (lowEnough ? " can" : " can not") 
                + " be automatically processed by system");

        return lowEnough;
    }

}

Note that this is a standard POJO that we can easily unit test it! As I’ve said before – Spring Integration doesn’t tightly couple us to its messaging facilities. For the sake of brevity, I am not pasting unit tests in this tutorial – but if you’re interested go ahead and download github project and see the tests for yourself.

Let’s specify input/output channels for the messaging layer and hook the filter in. Add the following code to the integration schema config:

<int:filter
    input-channel="singleInvoicesChannel"
    output-channel="filteredInvoicesChannel"
    ref="invoiceFilter" />

<int:channel id = "filteredInvoicesChannel" />

4. Routing invoices

So far, we’ve splitted and filtered out some invoices. Now it’s time to inspect contents of the each invoice more closely and decide, whether it is an invoice issued from the current country (local), or from an another country (foreign). In order to do that we can approach as before and use custom class for routing logic. We will (for the sake of demonstration purposes) take the other approach now – we will put Spring Expression Language (SpEL) to use and handle routing completely declaratively. Remember isForeign method on Invoice class? We can directly invoke it with SpEL in router declaration (by using selector-expression attribute)! Router will take a look on the payload, evaluate whether it’s a foreign or a local invoice and forward it to the corresponding channel:

<int:recipient-list-router input-channel="filteredInvoicesChannel">
    <int:recipient channel = "foreignTransactions" selector-expression="payload.foreign" />
    <int:recipient channel = "localTransactions" selector-expression="!payload.foreign" />
</int:recipient-list-router>

<int:channel id = "foreignTransactions" />
<int:channel id = "localTransactions" />

We will continue developing this application in the second part of this tutorial.

Also interested in Apache Camel? Check out this tutorial implemented with Apache Camel instead of Spring Integration!

Let your IntelliJ IDEA generate good looking equals, hashCode and toString with Google Guava

THE PROBLEM

In the world of Java, we’re quite often in the need of writing equals, hashCode and toString methods. To be honest, this is usually only an boilerplate obligation.

Thanks to smart IDEs, we don’t usually do this by ourselves anymore. We just let and IDE to do the hard work. There is one problem though. The generated code is usually incredibly ugly. Let’s consider following POJO:

public class Beer {
    
    private String brand;
    private String type;
    private int degrees;
    private double alcoholPercentage;
	private List<String> ingredients;

	// constructor
	// getters, setters if needed
    
}

THE USUAL SOLUTION

All of the major IDEs have the ability of generating the methods I mentioned, but this is how hashCode, equals and toString would look like:

1. equals – long list of IF statements …

@Override
public boolean equals(final Object o) {
	if (this == o) {
		return true;
	}
	if (!(o instanceof Beer)) {
		return false;
	}

	final Beer beer = (Beer) o;

	if (Double.compare(beer.alcoholPercentage, alcoholPercentage) != 0) {
		return false;
	}
	if (degrees != beer.degrees) {
		return false;
	}
	if (!brand.equals(beer.brand)) {
		return false;
	}
	if (!ingredients.equals(beer.ingredients)) {
		return false;
	}
	if (!type.equals(beer.type)) {
		return false;
	}

	return true;
}

2. hashCode – confusing magic numbers, xors and shifts

@Override
public int hashCode() {
    int result;
    long temp;
    result = brand.hashCode();
    result = 31 * result + type.hashCode();
    result = 31 * result + degrees;
    temp = alcoholPercentage != +0.0d ? Double.doubleToLongBits(alcoholPercentage) : 0L;
    result = 31 * result + (int) (temp ^ (temp >>> 32));
    result = 31 * result + ingredients.hashCode();
    return result;
}

3. toString – nasty string concatenation

@Override
public String toString() {
    return "Beer{" +
            "brand='" + brand + '\'' +
            ", type='" + type + '\'' +
            ", degrees=" + degrees +
            ", alcoholPercentage=" + alcoholPercentage +
            ", ingredients=" + ingredients +
            '}';
}

SOLUTION WITH GOOGLE GUAVA

Maybe you’ve heard of Google Guava. Maybe you’re already using it. Anyway, Google Guava is a nice little library that provides lot of goodies for Java. Using Guava we can rewrite three methods above to better looking alternatives:

1. equals – army of IF statements transformed to chained call

@Override
public boolean equals(final Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null || getClass() != obj.getClass()) {
        return false;
    }
    final Beer other = (Beer) obj;
    return Objects.equal(this.brand, other.brand) && Objects
            .equal(this.type, other.type) && Objects
            .equal(this.degrees, other.degrees) && Objects
            .equal(this.alcoholPercentage, other.alcoholPercentage) && Objects
            .equal(this.ingredients, other.ingredients);
}

2. hashCode – one-liner

@Override
public int hashCode() {
    return Objects.hashCode(brand, type, degrees, alcoholPercentage, ingredients);
}

3. toString – consistent chained call

@Override
public String toString() {
    return Objects.toStringHelper(this)
            .add("brand", brand)
            .add("type", type)
            .add("degrees", degrees)
            .add("alcoholPercentage", alcoholPercentage)
            .add("ingredients", ingredients)
            .toString();
}

SETUP YOUR INTELLIJ IDEA

For the equals and hashCode, there is a plugin called Equals and HashCode Deluxe Generator from Michal Jedynak. You can install it directly within IntelliJ, just type CTRL + SHIFT + A (or CMD + SHIFT + A on Mac) and type Browse repositories. That should bring you to following dialog where you can search for the plugin:

IntelliJ IDEA Plugins Repository

Using the new equals and hashCode plugins is simple, you will have new context menu option equals() and hashCode() deluxe directly next to the old one. Just press ALT+INS (or CTRL+N on Mac) and you will see familiar generate menu:

IntelliJ IDEA Generate menu

As far as toString is concerned, we only have to create a new template in IntelliJ. Press ALT + INS and go to toString() menu option. Click on the Settings button and navigate to Templates tab. In the Templates tab click the + button:

IntelliJ IDEA toString template

Give to the new template a name (like Guava toString or so) and paste the following code into the editor:

public String toString() {
    #set ($autoImportPackages = "com.google.common.base.Objects")
    return Objects.toStringHelper(this)
    #foreach ($member in $members)
        .add("$member.name", $member.accessor)
    #end
        .toString();
}

Using new template is easy, just enter generate menu (ALT + INS), select toString() and be sure to pick the right template:

IntelliJ IDEA toString() template

And that’s it, enjoy!

Part 6 – Adding AOP support

I heard a story about one senior (and quite highly paid) softwaree engineer. He was given task to log every method in every controller in project he was working on. Engineer rewrote all controller methods, so from code like this:

    @RequestMapping(method = RequestMethod.GET)
    public String showEmployees(Model model) {
        List<Employee> employees = employeeDao.list();
        model.addAttribute("employees", employees);

        return "employees/list";
    }

he made following code:

    @RequestMapping(method = RequestMethod.GET)
    public String showEmployees(Model model) {
	LOGGER.log("Invoking method showEmployees");

        List<Employee> employees = employeeDao.list();
        model.addAttribute("employees", employees);

	LOGGER.log("Returning from method showEmployees");
        return "employees/list";
    }

What’s wrong with this code? Well:

  • It takes lot of time to alter every method with such code
  • It is error prone – you can introduce typos or forget to add logging somewhere
  • It is mixing cross-cutting concerns. That means you are adding same kind of repetetive, boilerplate and unrelated code to places where it doesn’t belong.
  • For example, what is the responsibility of showEmployees method? It is invoking service, getting employees and putting them to model. Logging really isn’t it’s responsibility, so why to mix those concerns?

    If engineer I mentioned knew about Aspect Oriented Programming he would save lot of time and made code better and more readable. Spring supports something called “Aspects” that are made exactly for such a problems. Aspects allow us to define common functionality in one place. Before we write any code, there is some terminology to understand. This terminology is quite huge and I am not going to write it here, but I encourage you to read Spring’s official reference page on AOP if you wish to know more. You should at least understand what is Advice, Join Point, Pointcut, Aspect and Weaving.

    OK let’s add Aspect for logging controller methods, exactly what should’ve done engineer from the story in the beginning.

    We must first add dependencies to pom.xml on AspectJ library:

            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
                <version>1.6.11</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjtools</artifactId>
                <version>1.6.11</version>
            </dependency>
    

    Also check if you have dependency on Spring’s AOP (but if you follow this tutorial from the very beginning you already have it):

    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-aop</artifactId>
    			<version>3.1.0.RELEASE</version>
    		</dependency>
    

    Now let’s write Aspect’s code. Create package org.timesheet.aspects and add ControllerLoggingAspect class:

    package org.timesheet.aspects;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    
    import java.util.Arrays;
    
    /**
     * Will log every invokation of @RequestMapping annotated methods
     * in @Controller annotated beans.
     */
    @Aspect
    public class ControllerLoggingAspect {
    
        @Pointcut("within(@org.springframework.stereotype.Controller *)")
        public void controller() {}
    
        @Pointcut("execution(* *(..))")
        public void methodPointcut() {}
    
        @Pointcut("within(@org.springframework.web.bind.annotation.RequestMapping *)")
        public void requestMapping() {}
    
        @Before("controller() && methodPointcut() && requestMapping()")
        public void aroundControllerMethod(JoinPoint joinPoint) throws Throwable {
            System.out.println("Invoked: " + niceName(joinPoint));
        }
    
        @AfterReturning("controller() && methodPointcut() && requestMapping()")
        public void afterControllerMethod(JoinPoint joinPoint) {
            System.out.println("Finished: " + niceName(joinPoint));
        }
    
        private String niceName(JoinPoint joinPoint) {
            return joinPoint.getTarget().getClass()
                    + "#" + joinPoint.getSignature().getName()
                    + "\n\targs:" + Arrays.toString(joinPoint.getArgs());
        }
    
    }
    

    This code says, that @Before and @AfterReturning from controller method we will log information about it’s invokation (name and arguments). This advices execute when all three pointcuts are matching. controller() pointcut marks matching join point (that matches stereotype Controller) at which advice should be woven. methodPointcut() marks that we’re dealing with method call and requestMapping() pointcut marks methods annotated with @RequestMapping.

    To make it work, we’ll add aop.xml Spring configuration file under src/main/resources:

    <?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:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
    
        <!-- AOP support -->
        <bean id="controllerAspect" class="org.timesheet.aspects.ControllerLoggingAspect" />
        <aop:aspectj-autoproxy>
            <aop:include name="controllerAspect" />
        </aop:aspectj-autoproxy>
    
    </beans>
    

    And then we’ll import it in timesheet-servlet.xml Spring config:

    <import resource="classpath:aop.xml" />
    

    This was the last part of tutorial. I hope you have now better understanding of what Spring is and how does it help to solve your problems. Remember that we’ve covered only tiny piece of Spring in this tutorial. There is still much more to explore!

    Back to main tutorial page

Part 4 – Adding Spring MVC – part 1

Welcome to the fourth part of this tutorial. In this part, we will write controllers and views using Spring MVC and think about our REST model.

First thing that we must do, is make a web application from what we have so far. We will add web/WEB-INF folder to our project root. Inside WEB-INF create jsp folder. We will put our JSPs in that place. Inside that folder we will put deployment descriptor web.xml file with following contents:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"

         version="3.0">

    <display-name>timesheet-app</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:persistence-beans.xml
        </param-value>
    </context-param>

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

    <servlet>
        <servlet-name>timesheet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>timesheet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

Note that we are using servlet called timesheet. This is a dispatcher servlet. The following picture illustrates how Spring’s dispatcher servlet works (it’s called Front controller on picture below):

dispatcher servlet

  1. Request is handled by dispatcher servlet
  2. Dispatcher servlet decides to which controller it should deliver request (by request mapping, we’ll see that later) and then it delegates request
  3. Controller creates model and delivers it back to dispatcher servlet
  4. Dispatcher servlet resolves logical name of the view, binds model there and renders the view

The last step is quite mysterious. How does dispatcher servlet resolve logical name of the view? It uses something called ViewResolver. But we are not going to create our own by hand, instead we will just create another configuration file, define a bean with ViewResolver and have it injected by Spring. In WEB-INF create another Spring configuration file. By convention it must be named timesheet-servlet.xml, because we named our DispatcherServlet “timesheet” and this is file name, where Spring will be looking for config by default. Also create package org.timesheet.web. This is where we will be putting our controllers (which are again only annotated POJOs).

Here’s the timesheet-servlet.xml

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <context:component-scan base-package="org.timesheet.web" />

    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

We defined prefix and suffix for resolving logical names. It is really simple. We calculate name like this:
full name = prefix + logical name + suffix
Therefore with our InternalResourceViewResolver, logical name “index” would be resolved to “/WEB-INF/jsp/index.jsp”.

For views we will be using JSP technology with JSTL (tag library), so we need to add another dependencies to our pom.xml file:

        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
        </dependency>

Now, we’d like to handle GET on /timesheet-app/welcome. So we need to write view and controller (for Model we’ll use one from Spring facilities). Let’s start with controller:

package org.timesheet.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.Date;

@Controller
@RequestMapping("/welcome")
public class WelcomeController {

    @RequestMapping(method = RequestMethod.GET)
    public String showMenu(Model model) {
        model.addAttribute("today", new Date());
        return "index";
    }

}

So when someone accesses url welcome (in our case http://localhost:8080/timesheet-app/welcome), this controller will handle request. We are also using Model and binding there value with name “today”. This is how we get value to view page.

Note that root of my application is /timesheet-app. It’s called application context. You can change that of course, but all remaining codes asume that you’re application context is set like that. If you’re deploying WAR it’s based on WAR’s name.

From showMenu method we’re returning “index” – which will be resolved to WEB-INF/jsp/index.jsp so let’s create such a page and put there some basic content:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<html>
<head>
    <title>Welcome to Timesheet app!</title>
</head>
<body>
    <h1>Welcome to the Timesheet App!</h1>

    <ul>
        <li><a href="managers">List managers</a></li>
        <li><a href="employees">List employees</a></li>
        <li><a href="tasks">List tasks</a></li>
        <li><a href="timesheets">List timesheets</a></li>
    </ul>

    <h2>Also check out <a href="timesheet-service">extra services!</a></h2>
    Today is: <fmt:formatDate value="${today}" pattern="dd-MM-yyyy" />
</body>
</html>

OK to recap, we added web.xml file, timesheet-servlet.xml Spring configuration file, controller class and jsp page. Let’s try to run this on some web container. I’ll be using Tomcat7, but if you feel more comfortable with another web container or even application server – feel free to switch. Now there are plenty of ways how to run Tomcat and how to deploy the application. You can:

Whichever you choose, be sure that you can access URL mentioned above before continuing. Honestly, deployment in Java is least fun for me, so don’t give up if you feel frustrated, it might not work for the first time. But with tutorials above, you probably won’t have any problems. Also don’t forget about setting right application context.

Before we write more controllers, let’s prepare some data. When Spring creates welcomeController bean, we’d like to have some data. So for now, let’s just write dummy generator that will just create some entites. Later in tutorial, we will see some more realistic solution.

Put helpers package under web package were controllers are place there EntityGenerator class:

package org.timesheet.web.helpers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.timesheet.domain.Employee;
import org.timesheet.domain.Manager;
import org.timesheet.domain.Task;
import org.timesheet.domain.Timesheet;
import org.timesheet.service.GenericDao;
import org.timesheet.service.dao.EmployeeDao;
import org.timesheet.service.dao.ManagerDao;
import org.timesheet.service.dao.TaskDao;
import org.timesheet.service.dao.TimesheetDao;

import java.util.List;

/**
 * Small util helper for generating entities to simulate real system.
 */
@Service
public final class EntityGenerator {

    @Autowired
    private EmployeeDao employeeDao;

    @Autowired
    private ManagerDao managerDao;

    @Autowired
    private TaskDao taskDao;

    @Autowired
    private TimesheetDao timesheetDao;

    public void generateDomain() {
        Employee steve = new Employee("Steve", "Design");
        Employee bill = new Employee("Bill", "Marketing");
        Employee linus = new Employee("Linus", "Programming");
        
        // free employees (no tasks/timesheets)
        Employee john = new Employee("John", "Beatles");
        Employee george = new Employee("George", "Beatles");
        Employee ringo = new Employee("Ringo", "Beatles");
        Employee paul = new Employee("Paul", "Beatles");

        Manager eric = new Manager("Eric");
        Manager larry = new Manager("Larry");
        
        // free managers
        Manager simon = new Manager("Simon");
        Manager garfunkel = new Manager("Garfunkel");

        addAll(employeeDao, steve, bill, linus, john, george, ringo, paul);
        addAll(managerDao, eric, larry, simon, garfunkel);

        Task springTask = new Task("Migration to Spring 3.1", eric, steve, linus);
        Task tomcatTask = new Task("Optimizing Tomcat", eric, bill);
        Task centosTask = new Task("Deploying to CentOS", larry, linus);

        addAll(taskDao, springTask, tomcatTask, centosTask);

        Timesheet linusOnSpring = new Timesheet(linus, springTask, 42);
        Timesheet billOnTomcat = new Timesheet(bill, tomcatTask, 30);

        addAll(timesheetDao, linusOnSpring, billOnTomcat);
    }
    
    public void deleteDomain() {
        List<Timesheet> timesheets = timesheetDao.list();
        for (Timesheet timesheet : timesheets) {
            timesheetDao.remove(timesheet);
        }

        List<Task> tasks = taskDao.list();
        for (Task task : tasks) {
            taskDao.remove(task);
        }

        List<Manager> managers = managerDao.list();
        for (Manager manager : managers) {
            managerDao.remove(manager);
        }

        List<Employee> employees = employeeDao.list();
        for (Employee employee : employees) {
            employeeDao.remove(employee);
        }
    }
    
    private <T> void addAll(GenericDao<T, Long> dao, T... entites) {
        for (T o : entites) {
            dao.add(o);
        }
    }
}

Now let’s use the code for WelcomeController. We’ll inject there generator and place special method annotated with @PostConstruct annotation. That’s JSR-250 annotation for bean lifecycle and Spring supports it. It means, that this method will be invoked right after Spring IoC container instantiates welcomeController bean.

package org.timesheet.web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.timesheet.web.helpers.EntityGenerator;

import javax.annotation.PostConstruct;
import java.util.Date;

@Controller
@RequestMapping("/welcome")
public class WelcomeController {

    @Autowired
    private EntityGenerator entityGenerator;

    @RequestMapping(method = RequestMethod.GET)
    public String showMenu(Model model) {
        model.addAttribute("today", new Date());
        return "index";
    }

    @PostConstruct
    public void prepareFakeDomain() {
        entityGenerator.deleteDomain();
        entityGenerator.generateDomain();
    }
    
}

OK let’s write some controllers for domain logic now!

We’ll start by writing Employee’s controller. First, create class EmployeeController under org.timesheet.web package. Mark class as web controller and to handle “/employees” requests:

@Controller
@RequestMapping("/employees")
public class EmployeeController { ... 

For processing persistent data (Employees in this case) we need DAO and have it autowired by Spring’s IoC container, so let’s do just that:

    private EmployeeDao employeeDao;

    @Autowired
    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }

Now we want to handle HTTP GET method. When user accesses http://localhost:8080/timesheet-app/employees with a web browser, controller must handlet GET request. It’s only contacting DAO and collecting all employees and putting them into the model.

@RequestMapping(method = RequestMethod.GET)
    public String showEmployees(Model model) {
        List<Employee> employees = employeeDao.list();
        model.addAttribute("employees", employees);

        return "employees/list";
    }

Under jsp folder create employees folder where we will put all corresponding JSPs of employees. Probably you already noticed, that page with list of employees will be resolved to /WEB-INF/jsp/employees/list.jsp. So create such a page. We’ll see the contents later, if you want you can put there random text for now to see if it works.

In JSP page we will show a link next to employee for his personal page, which will look like http://localhost:8080/timesheet-app/employees/{id} where ID is employee’s id. This is RESTful URL because it’s resource oriented, and we’re directly identifying resource. RESTless URL would be something like http://localhost:8080/timesheet-app/employees.html?id=123. That’s action oriented and doesn’t identify resource.

Let’s add another method to controller, that handles this URL:

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String getEmployee(@PathVariable("id") long id, Model model) {
        Employee employee = employeeDao.find(id);
        model.addAttribute("employee", employee);

        return "employees/view";
    }

Again, create view.jsp page under /jsp/employees folder. From this page, we’d also like to alter employee. We’re simply access same URL but with different web method – POST. That means, we’re giving data from bounded model to update.

This method handles employee updating:

    @RequestMapping(value = "/{id}", method = RequestMethod.POST)
    public String updateEmployee(@PathVariable("id") long id, Employee employee) {
        employee.setId(id);
        employeeDao.update(employee);

        return "redirect:/employees";
    }

In this case, we were accessing employees/{id} with GET or POST method. But what if we want to delete employee? We’ll access same URL but with different method – DELETE. We will use additional business logic in EmployeeDao. If anything goes wrong, we’ll throw exception containing employee that cannot be deleted. So le’ts add controller method for this case:

    /**
     * Deletes employee with specified ID
     * @param id Employee's ID
     * @return redirects to employees if everything was ok
     * @throws EmployeeDeleteException When employee cannot be deleted
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public String deleteEmployee(@PathVariable("id") long id)
            throws EmployeeDeleteException {

        Employee toDelete = employeeDao.find(id);
        boolean wasDeleted = employeeDao.removeEmployee(toDelete);

        if (!wasDeleted) {
            throw new EmployeeDeleteException(toDelete);
        }

        // everything OK, see remaining employees
        return "redirect:/employees";
    }

Notice that we’re returning redirect from that method. The redirect: prefix signals that the request should be redirected to the path that it precedes.

Create package org.timesheet.web.exceptions and place following EmployeeDeleteException there:

package org.timesheet.web.exceptions;

import org.timesheet.domain.Employee;

/**
 * When employee cannot be deleted.
 */
public class EmployeeDeleteException extends Exception {

    private Employee employee;

    public EmployeeDeleteException(Employee employee) {
        this.employee = employee;
    }

    public Employee getEmployee() {
        return employee;
    }
}

Arguably, this exception could directly be thrown from DAO. Now how do we handle it? Spring has special annotation called @ExceptionHandler. We’ll place it in our controller and when specified exception is thrown, method annotated with ExceptionHandler will handle it and resolve correct view:

    /**
     * Handles EmployeeDeleteException
     * @param e Thrown exception with employee that couldn't be deleted
     * @return binds employee to model and returns employees/delete-error
     */
    @ExceptionHandler(EmployeeDeleteException.class)
    public ModelAndView handleDeleteException(EmployeeDeleteException e) {
        ModelMap model = new ModelMap();
        model.put("employee", e.getEmployee());
        return new ModelAndView("employees/delete-error", model);
    }

Okay, time for JSPs. We will be using some resources (like *.css or *.js) so in your web application root create resources folder. WEB-INF is NOT root, it’s the folder above. So resources and WEB-INF now should be in same level in directory tree. We’ve configured our dispatcher servlet to handle every request (with / url pattern), but we don’t want to have static resources handled by it atm. We’ll resolve that by simply putting mapping for default servlet in our web.xml file:

    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/resources/*</url-pattern>
    </servlet-mapping>

Under those resource create styles.css file. We’ll put CSS for whole app there right now even though we will use lotsa that stuff later.

table, th {
    margin: 10px;
    padding: 5px;
    width: 300px;
}

.main-table {
    border: 2px solid green;
    border-collapse: collapse;
}

.wide {
    width: 600px;
}

.main-table th {
    background-color: green;
    color: white;
}

.main-table td {
    border: 1px solid green;
}

th {
    text-align: left;
}

h1 {
    margin: 10px;
}

a {
    margin: 10px;
}

label {
    display: block;
    text-align: left;
}

#list {
    padding-left: 10px;
    position: relative;
}

#list ul {
    padding: 0;
}

#list li {
    list-style: none;
    margin-bottom: 1em;
}

.hidden {
    display: none;
}

.delete {
    margin: 0;
    text-align: center;
}

.delete-button {
    border: none;
    background: url('/timesheet-app/resources/delete.png') no-repeat top left;
    color: transparent;
    cursor: pointer;
    padding: 2px 8px;
}

.task-table {
    width: 150px;
    border: 1px solid #dcdcdc;
}

.errors {
    color: #000;
    background-color: #ffEEEE;
    border: 3px solid #ff0000;
    padding: 8px;
    margin: 16px;
}

Let’s now create employess/list.jsp page:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form"%>

<html>
<head>
    <title>Employees</title>
    <link rel="stylesheet" href="/timesheet-app/resources/style.css" type="text/css">
</head>
<body>
    <h1>List of employees</h1>
    <a href="employees?new">Add new employee</a>
    <table cellspacing="5" class="main-table">
        <tr>
            <th>Name</th>
            <th>Department</th>
            <th>Details</th>
            <th>Delete</th>
        </tr>
        <c:forEach items="#{employees}" var="emp">
            <tr>
                <td>${emp.name}</td>
                <td>${emp.department}</td>
                <td>
                    <a href="employees/${emp.id}">Go to page</a>
                </td>
                <td>
                    <sf:form action="employees/${emp.id}" method="delete" cssClass="delete">
                        <input type="submit" class="delete-button" value="" />
                    </sf:form>
                </td>
            </tr>
        </c:forEach>
    </table>

    <br />
    <a href="welcome">Go back</a>
</body>
</html>

From that page, we’re linking our css under resources (with full name including application context). There’s also link linking to employee’s detail page (view.jsp) that’s resolved from employee’s id.
The most interesting part is usage of sf taglib. To stay Web 1.0 friendly, we unfortunatelly cannot directly use DELETE. Up to HTML4 and XHTML1, html forms can only use GET and POST. Workaround is to use hidden field that marks, if the POST actually should be used as DELETE. That’s precisely what Spring does for us for free – just using sf:form prefix. So we’re tunneling DELETE via HTTP POST, but it will be dispatched correctly.
To make this work, we must put in our web.xml special Spring’s filter for that:

    <filter>
        <filter-name>httpMethodFilter</filter-name>
        <filter-class>
            org.springframework.web.filter.HiddenHttpMethodFilter
        </filter-class>
    </filter>
    
    <filter-mapping>
        <filter-name>httpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Even though that JSP is java specific technology that is actually compiled to servlet, we can use it pretty much like any HTML page. We’ve added some CSS and now we add most popular javascript library – jQuery. Go to https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js and download jquery.js file and drop it to your resources folder. We will allow users to update resource with POST, so we will use jQuery for some DOM manipulations – just for the sake of fancyness. You can use pretty much anything you can in common HTML pages.

Let’s now create /employees/view.jsp – that’s kinda detail page for employee.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form"%>

<html>
<head>
    <title>Employee page</title>
    <link rel="stylesheet" href="/timesheet-app/resources/style.css" type="text/css">
</head>
<body>
    <h2>Employee info</h2>
    <div id="list">
        <sf:form method="post">
            <ul>
                <li>
                    <label for="name">Name:</label>
                    <input name="name" id="name" value="${employee.name}" disabled="true"/>
                </li>
                <li>
                    <label for="department">Department:</label>
                    <input name="department" id="department" value="${employee.department}" disabled="true" />
                </li>
                <li>
                    <input type="button" value="Unlock" id="unlock" />
                    <input type="submit" value="Save" id="save" class="hidden" />
                </li>
            </ul>
        </sf:form>
    </div>

    <br /><br />
    <a href="../employees">Go Back</a>

    <script src="/timesheet-app/resources/jquery-1.7.1.js"></script>
    <script>
        (function() {
            $("#unlock").on("click", function() {
                $("#unlock").addClass("hidden");

                // enable stuff
                $("#name").removeAttr("disabled");
                $("#department").removeAttr("disabled");
                $("#save").removeClass("hidden");
            });
        })();
    </script>
</body>
</html>

Inside the page we refer to jQuery file and we have self-invoking annonymous function – after button with id “unlock” is clicked, we hide it, bring forward the submit button and unlock fields so employee can be updated. After Save button is pressed, we’re redirected back to list of employees and this one has been updated.

The last functionality that we’re gonna get done for CRUD on Employee is adding. We will handle that by accessing employees with GET and extra prameter that we’ll call new. So URL for adding employee will be: http://localhost:8080/timesheet-app/employees?new
Let’s modify our controller for this:

    @RequestMapping(params = "new", method = RequestMethod.GET)
    public String createEmployeeForm(Model model) {
        model.addAttribute("employee", new Employee());
        return "employees/new";
    }

That will serve new JSP page – /WEB-INF/jsp/employees/new.jsp:

<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Add new employee</title>
    <link rel="stylesheet" href="/timesheet-app/resources/style.css" type="text/css">
</head>
<body>
    <h2>Add new Employee</h2>
    <div id="list">
        <sf:form method="post" action="employees">
            <ul>
                <li>
                    <label for="name">Name:</label>
                    <input name="name" id="name" value="${employee.name}"/>
                </li>
                <li>
                    <label for="department">Department:</label>
                    <input name="department" id="department"
                           value="${employee.department}" />
                </li>
                <li>
                    <input type="submit" value="Save" id="save" />
                </li>
            </ul>
        </sf:form>
    </div>

    <br /><br />
    <a href="employees">Go Back</a>
</body>
</html>

The page is pretty similar to view.jsp. In real world applications we’d use somerhing like Apache Tiles for reducing redundant code, but now let’s not worry about that.

Note that we submit form with “employees” action. Back to our controller, let’s handle employees with POST http method:

    @RequestMapping(method = RequestMethod.POST)
    public String addEmployee(Employee employee) {
        employeeDao.add(employee);

        return "redirect:/employees";
    }

And let’s not forget about error JSP page, when we cannot delete employee, jsp/employees/delete-error.jsp:

<html>
<head>
    <title>Cannot delete employee</title>
</head>
<body>
    Oops! Resource <a href="${employee.id}">${employee.name}</a> can not be deleted.

    <p>
        Make sure employee doesn't have assigned any task or active timesheet.
    </p>

    <br /><br /><br />
    <a href="../welcome">Back to main page.</a>
</body>
</html>

That’s it, we have whole CRUD functionality for our employees. Let’s recap the basic steps what we just did:

  • Added EmployeeController class
  • Create resources folder in web root for static content
  • Added mapping for default servlet in web.xml
  • Added styles.css to resources folder
  • Configured POST-DELETE tunneling with filter in web.xml
  • Downloaded jQuery.js and added to our resources folder
  • Added employess/list.jsp page
  • Added employess/view.jsp page
  • Added employess/new.jsp page
  • Added employees/delete-error.jsp page

Now, here’s complete code for the controller:

package org.timesheet.web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.timesheet.domain.Employee;
import org.timesheet.service.dao.EmployeeDao;
import org.timesheet.web.exceptions.EmployeeDeleteException;

import java.util.List;

/**
 * Controller for handling Employees.
 */
@Controller
@RequestMapping("/employees")
public class EmployeeController {

    private EmployeeDao employeeDao;

    @Autowired
    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }

    public EmployeeDao getEmployeeDao() {
        return employeeDao;
    }

    /**
     * Retrieves employees, puts them in the model and returns corresponding view
     * @param model Model to put employees to
     * @return employees/list
     */
    @RequestMapping(method = RequestMethod.GET)
    public String showEmployees(Model model) {
        List<Employee> employees = employeeDao.list();
        model.addAttribute("employees", employees);

        return "employees/list";
    }

    /**
     * Deletes employee with specified ID
     * @param id Employee's ID
     * @return redirects to employees if everything was ok
     * @throws EmployeeDeleteException When employee cannot be deleted
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public String deleteEmployee(@PathVariable("id") long id)
            throws EmployeeDeleteException {

        Employee toDelete = employeeDao.find(id);
        boolean wasDeleted = employeeDao.removeEmployee(toDelete);

        if (!wasDeleted) {
            throw new EmployeeDeleteException(toDelete);
        }

        // everything OK, see remaining employees
        return "redirect:/employees";
    }

    /**
     * Handles EmployeeDeleteException
     * @param e Thrown exception with employee that couldn't be deleted
     * @return binds employee to model and returns employees/delete-error
     */
    @ExceptionHandler(EmployeeDeleteException.class)
    public ModelAndView handleDeleteException(EmployeeDeleteException e) {
        ModelMap model = new ModelMap();
        model.put("employee", e.getEmployee());
        return new ModelAndView("employees/delete-error", model);
    }

    /**
     * Returns employee with specified ID
     * @param id Employee's ID
     * @param model Model to put employee to
     * @return employees/view
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String getEmployee(@PathVariable("id") long id, Model model) {
        Employee employee = employeeDao.find(id);
        model.addAttribute("employee", employee);

        return "employees/view";
    }

    /**
     * Updates employee with specified ID
     * @param id Employee's ID
     * @param employee Employee to update (bounded from HTML form)
     * @return redirects to employees
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.POST)
    public String updateEmployee(@PathVariable("id") long id, Employee employee) {
        employee.setId(id);
        employeeDao.update(employee);

        return "redirect:/employees";
    }

    /**
     * Creates form for new employee
     * @param model Model to bind to HTML form
     * @return employees/new
     */
    @RequestMapping(params = "new", method = RequestMethod.GET)
    public String createEmployeeForm(Model model) {
        model.addAttribute("employee", new Employee());
        return "employees/new";
    }

    /**
     * Saves new employee to the database
     * @param employee Employee to save
     * @return redirects to employees
     */
    @RequestMapping(method = RequestMethod.POST)
    public String addEmployee(Employee employee) {
        employeeDao.add(employee);

        return "redirect:/employees";
    }
    
}

If you’re using SpringSource Tool Suite, you can check mappings directly in IDE. Add to your project “Spring Project Nature”, in Properties->Spring->Bean Support configure your Spring’s config file. Then right click project and press Spring Tools->Show Request Mappings and you should see something like this:

Last thing about employees that remains is writing JUnit test. Since we have our timesheet-servlet.xml in WEB-INF, we can’t access its beans in JUnit test. What we’ll do is remove following line from timesheet-servlet.xml:

<context:component-scan base-package="org.timesheet.web" />

We now create new Spring’s bean config in src/main/resources and call it controllers.xml. The only thing we care about is to put autoscanning for controllers here, so content is pretty simple:

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <context:component-scan base-package="org.timesheet.web" />

</beans>

To make context aware of those spring beans, alter context-param in web.xml like this:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:persistence-beans.xml
            classpath:controllers.xml
        </param-value>
    </context-param>

Also, we now must import beans from controllers.xml to timesheet-servlet.xml, so instead of removed line <context:component-scan … from timesheet-servlet.xml, we add following:

<import resource="classpath:controllers.xml" />

That’ll allow us to have controllers autowired to tests. Okay, so in the test source folder create package org.timesheet.web and let’s put there EmployeeControllerTest. It’s pretty straightforward and we only test controller as POJO and how does it affect persistence layer (verifying via DAO). We made one exception however. In method testDeleteEmployeeThrowsException, we will explicitelly tell DAO to return false when trying to delete employee. This will save us complicated object creation and injection of additional DAOs. We will use popular mocking framework for this – Mockito.

Add dependency to your pom.xml:

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.9.0</version>
        </dependency>

Test for EmployeeController:

package org.timesheet.web;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
import org.springframework.web.servlet.ModelAndView;
import org.timesheet.DomainAwareBase;
import org.timesheet.domain.Employee;
import org.timesheet.service.dao.EmployeeDao;
import org.timesheet.web.exceptions.EmployeeDeleteException;

import java.util.Collection;
import java.util.List;

import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@ContextConfiguration(locations = {"/persistence-beans.xml", "/controllers.xml"})
public class EmployeeControllerTest extends DomainAwareBase {
    
    @Autowired
    private EmployeeDao employeeDao;

    @Autowired
    private EmployeeController controller;
    
    private Model model; // used for controller
    
    @Before
    public void setUp() {
        model = new ExtendedModelMap();
    }

    @After
    public void cleanUp() {
        List<Employee> employees = employeeDao.list();
        for (Employee employee : employees) {
            employeeDao.remove(employee);
        }
    }

    @Test
    public void testShowEmployees() {
        // prepare some data
        Employee employee = new Employee("Lucky", "Strike");
        employeeDao.add(employee);

        // use controller
        String view = controller.showEmployees(model);
        assertEquals("employees/list", view);

        List<Employee> listFromDao = employeeDao.list();
        Collection<?> listFromModel = (Collection<?>) model.asMap().get("employees");

        assertTrue(listFromModel.contains(employee));
        assertTrue(listFromDao.containsAll(listFromModel));
    }
    
    @Test
    public void testDeleteEmployeeOk() throws EmployeeDeleteException {
        // prepare ID to delete
        Employee john = new Employee("John Lennon", "Singing");
        employeeDao.add(john);
        long id = john.getId();

        // delete & assert
        String view = controller.deleteEmployee(id);
        assertEquals("redirect:/employees", view);
        assertNull(employeeDao.find(id));
    }

    @Test(expected = EmployeeDeleteException.class)
    public void testDeleteEmployeeThrowsException() throws EmployeeDeleteException {
        // prepare ID to delete
        Employee john = new Employee("John Lennon", "Singing");
        employeeDao.add(john);
        long id = john.getId();

        // mock DAO for this call
        EmployeeDao mockedDao = mock(EmployeeDao.class);
        when(mockedDao.removeEmployee(john)).thenReturn(false);

        EmployeeDao originalDao = controller.getEmployeeDao();
        try {
            // delete & expect exception
            controller.setEmployeeDao(mockedDao);
            controller.deleteEmployee(id);
        } finally {
            controller.setEmployeeDao(originalDao);
        }
    }

    @Test
    public void testHandleDeleteException() {
        Employee john = new Employee("John Lennon", "Singing");
        EmployeeDeleteException e = new EmployeeDeleteException(john);
        ModelAndView modelAndView = controller.handleDeleteException(e);

        assertEquals("employees/delete-error", modelAndView.getViewName());
        assertTrue(modelAndView.getModelMap().containsValue(john));
    }
    
    @Test
    public void testGetEmployee() {
        // prepare employee
        Employee george = new Employee("George Harrison", "Singing");
        employeeDao.add(george);
        long id = george.getId();
        
        // get & assert
        String view = controller.getEmployee(id, model);
        assertEquals("employees/view", view);
        assertEquals(george, model.asMap().get("employee"));
    }

    @Test
    public void testUpdateEmployee() {
        // prepare employee
        Employee ringo = new Employee("Ringo Starr", "Singing");
        employeeDao.add(ringo);
        long id = ringo.getId();

        // user alters Employee in HTML form
        ringo.setDepartment("Drums");

        // update & assert
        String view = controller.updateEmployee(id, ringo);
        assertEquals("redirect:/employees", view);
        assertEquals("Drums", employeeDao.find(id).getDepartment());
    }

    @Test
    public void testAddEmployee() {
        // prepare employee
        Employee paul = new Employee("Paul McCartney", "Singing");
        
        // save but via controller
        String view = controller.addEmployee(paul);
        assertEquals("redirect:/employees", view);

        // employee is stored in DB
        assertEquals(paul, employeeDao.find(paul.getId()));
    }
}

Notice how do we use mocked dao for setting it in try/finally block. It’s just for that one call to ensure that correct exception is thrown. If you’ve never seen mocking, I definitelly suggest learning more about this technique. There are plenty of mocking frameworks. The one we picked – Mockito – comes with really neat syntax that heavily uses java static imports.

Now, Managers are pretty similar to Employees, so without any big problems, let’s add pretty similar stuff for managers:

First, create managers folder in WEB-INF/jsp.

Now let’s write controller and inject corresponding DAO:

@Controller
@RequestMapping("/managers")
public class ManagerController {

    private ManagerDao managerDao;

    @Autowired
    public void setManagerDao(ManagerDao managerDao) {
        this.managerDao = managerDao;
    }

    public ManagerDao getManagerDao() {
        return managerDao;
    }
}

Add method for listing managers:

    /**
     * Retrieves managers, puts them in the model and returns corresponding view
     * @param model Model to put employees to
     * @return managers/list
     */
    @RequestMapping(method = RequestMethod.GET)
    public String showManagers(Model model) {
        List<Manager> employees = managerDao.list();
        model.addAttribute("managers", employees);

        return "managers/list";
    }

Add list.jsp to jsp/managers:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form"%>

<html>
<head>
    <title>Managers</title>
    <link rel="stylesheet" href="/timesheet-app/resources/style.css" type="text/css">
</head>
<body>
    <h1>List of managers</h1>
    <a href="managers?new">Add new manager</a>
    <table cellspacing="5" class="main-table">
        <tr>
            <th>Name</th>
            <th>Details</th>
            <th>Delete</th>
        </tr>
        <c:forEach items="#{managers}" var="man">
            <tr>
                <td>${man.name}</td>
                <td>
                    <a href="managers/${man.id}">Go to page</a>
                </td>
                <td>
                    <sf:form action="managers/${man.id}" method="delete" cssClass="delete">
                        <input type="submit" value="" class="delete-button" />
                    </sf:form>
                </td>
            </tr>
        </c:forEach>
    </table>

    <br />
    <a href="welcome">Go back</a>
</body>
</html>

Add method for deleting managers:

    /**
     * Deletes manager with specified ID
     * @param id Manager's ID
     * @return redirects to managers if everything was OK
     * @throws ManagerDeleteException When manager cannot be deleted
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public String deleteManager(@PathVariable("id") long id)
            throws ManagerDeleteException {

        Manager toDelete = managerDao.find(id);
        boolean wasDeleted = managerDao.removeManager(toDelete);

        if (!wasDeleted) {
            throw new ManagerDeleteException(toDelete);
        }

        // everything OK, see remaining managers
        return "redirect:/managers";
    }

Exception when deleting fails:

package org.timesheet.web.exceptions;

import org.timesheet.domain.Manager;

/**
 * When manager cannot be deleted
 */
public class ManagerDeleteException extends Exception {

    private Manager manager;

    public ManagerDeleteException(Manager manager) {
        this.manager = manager;
    }

    public Manager getManager() {
        return manager;
    }
}

Method for handling this exception:

    /**
     * Handles ManagerDeleteException
     * @param e Thrown exception with manager that couldn't be deleted
     * @return binds manager to model and returns managers/delete-error
     */
    @ExceptionHandler(ManagerDeleteException.class)
    public ModelAndView handleDeleteException(ManagerDeleteException e) {
        ModelMap model = new ModelMap();
        model.put("manager", e.getManager());
        return new ModelAndView("managers/delete-error", model);
    }

Add method for getting manager’s page:

    /**
     * Returns manager with specified ID
     * @param id Managers's ID
     * @param model Model to put manager to
     * @return managers/view
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String getManager(@PathVariable("id") long id, Model model) {
        Manager manager = managerDao.find(id);
        model.addAttribute("manager", manager);

        return "managers/view";
    }

Add manager’s page view.jsp under jsp/managers:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form"%>

<html>
<head>
    <title>Manager page</title>
    <link rel="stylesheet" href="/timesheet-app/resources/style.css" type="text/css">
</head>
<body>
    <h2>Manager info</h2>
    <div id="list">
        <sf:form method="post">
            <ul>
                <li>
                    <label for="name">Name:</label>
                    <input name="name" id="name" value="${manager.name}" disabled="true"/>
                </li>
                <li>
                    <input type="button" value="Unlock" id="unlock" />
                    <input type="submit" value="Save" id="save" class="hidden" />
                </li>
            </ul>
        </sf:form>
    </div>

    <br /><br />
    <a href="../managers">Go Back</a>

    <script src="/timesheet-app/resources/jquery-1.7.1.js"></script>
    <script>
        (function() {
            $("#unlock").on("click", function() {
                $("#unlock").addClass("hidden");

                // enable stuff
                $("#name").removeAttr("disabled");
                $("#save").removeClass("hidden");
            });
        })();
    </script>
</body>
</html>

JSP page for handling error on deleting:

<html>
<head>
    <title>Cannot delete manager</title>
</head>
<body>
    Oops! Resource <a href="${manager.id}">${manager.name}</a> can not be deleted.

    <p>
        Make sure manager doesn't have assigned any task or active timesheet.
    </p>

    <br /><br /><br />
    <a href="../welcome">Back to main page.</a>
</body>
</html>

Add method for updating manager:

    /**
     * Updates manager with specified ID
     * @param id Manager's ID
     * @param manager Manager to update (bounded from HTML form)
     * @return redirects to managers
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.POST)
    public String updateManager(@PathVariable("id") long id, Manager manager) {
        manager.setId(id);
        managerDao.update(manager);

        return "redirect:/managers";
    }

Add method for returning new manager’s form:

    /**
     * Creates form for new manager
     * @param model Model to bind to HTML form
     * @return manager/new
     */
    @RequestMapping(params = "new", method = RequestMethod.GET)
    public String createManagerForm(Model model) {
        model.addAttribute("manager", new Manager());
        return "managers/new";
    }

Add page for new manager new.jsp under jsp/managers:

<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Add new manager</title>
    <link rel="stylesheet" href="/timesheet-app/resources/style.css" type="text/css">
</head>
<body>
    <h2>Add new Manager</h2>
    <div id="list">
        <sf:form method="post" action="managers">
            <ul>
                <li>
                    <label for="name">Name:</label>
                    <input name="name" id="name" value="${manager.name}"/>
                </li>
                <li>
                    <input type="submit" value="Save" id="save" />
                </li>
            </ul>
        </sf:form>
    </div>

    <br /><br />
    <a href="managers">Go Back</a>
</body>
</html>

And finally, add method for adding manager:

    /**
     * Saves new manager to the database
     * @param manager Manager to save
     * @return redirects to managers
     */
    @RequestMapping(method = RequestMethod.POST)
    public String addManager(Manager manager) {
        managerDao.add(manager);

        return "redirect:/managers";
    }

Ok last piece of code for this part is test case for ManagerController:

package org.timesheet.web;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
import org.springframework.web.servlet.ModelAndView;
import org.timesheet.DomainAwareBase;
import org.timesheet.domain.Manager;
import org.timesheet.service.dao.ManagerDao;
import org.timesheet.web.exceptions.ManagerDeleteException;

import java.util.Collection;
import java.util.List;

import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@ContextConfiguration(locations = {"/persistence-beans.xml", "/controllers.xml"})
public class ManagerControllerTest extends DomainAwareBase {
    
    @Autowired
    private ManagerDao managerDao;

    @Autowired
    private ManagerController controller;
    
    private Model model; // used for controller
    
    @Before
    public void setUp() {
        model = new ExtendedModelMap();
    }

    @After
    public void cleanUp() {
        List<Manager> managers = managerDao.list();
        for (Manager manager : managers) {
            managerDao.remove(manager);
        }
    }

    @Test
    public void testShowManagers() {
        // prepare some data
        Manager manager = new Manager("Bob Dylan");
        managerDao.add(manager);

        // use controller
        String view = controller.showManagers(model);
        assertEquals("managers/list", view);

        List<Manager> listFromDao = managerDao.list();
        Collection<?> listFromModel = (Collection<?>) model.asMap().get("managers");

        assertTrue(listFromModel.contains(manager));
        assertTrue(listFromDao.containsAll(listFromModel));
    }
    
    @Test
    public void testDeleteManagerOk() throws ManagerDeleteException {
        // prepare ID to delete
        Manager john = new Manager("John Lennon");
        managerDao.add(john);
        long id = john.getId();

        // delete & assert
        String view = controller.deleteManager(id);
        assertEquals("redirect:/managers", view);
        assertNull(managerDao.find(id));
    }

    @Test(expected = ManagerDeleteException.class)
    public void testDeleteManagerThrowsException() throws ManagerDeleteException {
        // prepare ID to delete
        Manager john = new Manager("John Lennon");
        managerDao.add(john);
        long id = john.getId();

        // mock DAO for this call
        ManagerDao mockedDao = mock(ManagerDao.class);
        when(mockedDao.removeManager(john)).thenReturn(false);

        ManagerDao originalDao = controller.getManagerDao();
        try {
            // delete & expect exception
            controller.setManagerDao(mockedDao);
            controller.deleteManager(id);
        } finally {
            controller.setManagerDao(originalDao);
        }
    }

    @Test
    public void testHandleDeleteException() {
        Manager john = new Manager("John Lennon");
        ManagerDeleteException e = new ManagerDeleteException(john);
        ModelAndView modelAndView = controller.handleDeleteException(e);

        assertEquals("managers/delete-error", modelAndView.getViewName());
        assertTrue(modelAndView.getModelMap().containsValue(john));
    }

    @Test
    public void testGetManager() {
        // prepare manager
        Manager george = new Manager("George Harrison");
        managerDao.add(george);
        long id = george.getId();
        
        // get & assert
        String view = controller.getManager(id, model);
        assertEquals("managers/view", view);
        assertEquals(george, model.asMap().get("manager"));
    }

    @Test
    public void testUpdateManager() {
        // prepare manager
        Manager ringo = new Manager("Ringo Starr");
        managerDao.add(ringo);
        long id = ringo.getId();

        // user alters manager in HTML form
        ringo.setName("Rango Starr");

        // update & assert
        String view = controller.updateManager(id, ringo);
        assertEquals("redirect:/managers", view);
        assertEquals("Rango Starr", managerDao.find(id).getName());
    }

    @Test
    public void testAddManager() {
        // prepare manager
        Manager paul = new Manager("Paul McCartney");
        
        // save but via controller
        String view = controller.addManager(paul);
        assertEquals("redirect:/managers", view);

        // manager is stored in DB
        assertEquals(paul, managerDao.find(paul.getId()));
    }
}

Request mappings looks like this now:

So in this part, we learned what is Spring MVC, how to use our entities as models, how to write controllers in POJO style, how RESTful design looks like, how to create views with JSPs and how to setup application for using CSS and JavaScript.

We wrote controllers for Employees and Managers. In the next part we’ll continue with writing controllers for Tasks and Timesheets. Make sure everything works well so far before you proceed to the next part.

Here’s src folder (only new stuff is expanded. Don’t worry about .iml files, they’re for IntelliJ):

Here’s web folder:



Back to main tutorial page