Reading text file line by line in Java

Reading text files from hard disk is quite common task in software development practice. Usually we are interested in processing it line by line. In this article I would like to present few popular ways how to do it easily using Java IO.

Reading whole file into memory

Reading a text file can be as simple as calling single Files.readAllLines() method:

Path path = Paths.get(fileName);
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
for (String line : lines) {
   System.out.println(line);
}

This method reads the whole file with given path and stores all its lines into an array of String. The rest of the code just prepares the path and prints the read lines.

While it may seem like a perfect method, it has one significant drawback. If the file being read is large, the created array will be very long. As a result memory usage of the application and the whole JVM will grow significantly which can reduce the performance of the application or in worse case kill the application with infamous OutOfMemoryError.

Therefore, usage of this method must be done with care and only in cases where the input file is small.

Reading file using BufferedReader

If you are going to read large file, you can resort to using BufferedReader class:

try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
    while (true) {
        String line = reader.readLine();
        if (line == null) {
           break;
        }
    System.out.println(line);
    }
}

First, we are creating a FileReader instance to access the given file. Because FileReader provides only very basic set of operations, we are wrapping it inside BufferedReader which has more convenient and richer interface. Finally, we call BufferedReader.readLine() method to read each line separately as long as this method returns non-null. Try-with-resources block ensures that the file is always closed at the end.

If using Java 7 or later, creation of BufferedReader can be simplified using Files class:

Path path = Paths.get(fileName);
try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
    while (true) {
        String line = reader.readLine();
        if (line == null) {
           break;
        }
    System.out.println(line);
    }
}

Reading file using Java 8 streams

Java 8 brought new streams feature into the standard library. The Files class was also extended with new Files.lines() method which opens the file with given path and creates a stream with all the lines of the file. Then the stream of lines can be processed as any other stream of String including filter, map/transform and aggregate operations. In our case we print the lines only:

Path path = Paths.get(fileName);
try (Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8)) {
    lines.forEachOrdered(System.out::println);
}

This method has the advantage over the first presented method because the file is not read into memory at once but lazily.

Conclusion

Usage of BufferedReader is the most popular and versatile method to read a text file. If you are sure that the file is small, you can use Files.readAllLines() method to simplify the source code. The last method utilizing the streams API is very promising but requires Java 8 runtime environment which is still not an option in many cases (e.g. legacy systems, compatibility, long-running projects).

As always the source code of the example is available at GitHub.

Posted in Java | Tagged , | Leave a comment

Iterating through Java list backwards

The most natural method of iterating through a list in Java is to start from the first element of the list and then move forward until the last one is reached. While this is the most common, it is sometimes necessary to do it otherwise – start from the last element and move in reverse direction. In this post I would like to show 4 different ways to do it easily.

This short class creates a simple list and calls 4 methods to print it in reverse order:

package com.example.iteratelistreverse;

import com.google.common.collect.Lists;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import org.apache.commons.collections.iterators.ReverseListIterator;

public class Main {

    public static void main(String[] args) throws IOException {
        List<String> list = new ArrayList<>();
        list.add("First");
        list.add("Second");
        list.add("Third");
        list.add("Fourth");
        list.add("Fifth");

        iterateIndexedAccess(list);
        iterateListIterator(list);
        iterateReverseListIterator(list);
        iterateListsReverse(list);
    }

    private static void iterateIndexedAccess(List<String> list) {
        System.out.print("\nFor loop with random access:\n");
        for (int i = list.size() - 1; i >= 0; i--) {
            System.out.println(list.get(i));
        }
    }

    private static void iterateListIterator(List<String> list) {
        System.out.print("\nList iterator:\n");
        ListIterator<String> it = list.listIterator(list.size());
        while (it.hasPrevious()) {
            System.out.println(it.previous());
        }
    }

    private static void iterateReverseListIterator(List<String> list) {
        System.out.print("\nApache ReverseListIterator:\n");
        Iterator<String> it = new ReverseListIterator(list);
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }

    private static void iterateListsReverse(List<String> list) {
        System.out.print("\nGuava List.reverse:\n");
        for (String v : Lists.reverse(list)) {
            System.out.println(v);
        }
    }

}

Method iterateIndexedAccess() shows the most common solution. In this method we are using the indexed access to the elements of the list which is very easy and obvious. It has also very good performance assuming that the instance of the list supports fast random access. It is the case for very popular ArrayList class.

However, other classes may not support fast random access. One of the examples is LinkedList class. For every List.get() method call this class has to iterate from the beginning (or alternatively from the end) of the list to find the element at given index which makes it very slow for large lists.

Method iterateListIterator() overcomes this problem by using a list iterator which can move forward and backward using ListIterator.next() and ListIterator.previous() calls respectively. In our example we are starting from the position after the last element and are moving to the previous element on every loop iteration.

Method iterateReverseListIterator() is quite specific because it creates an iterator which pretends that it is normal iterator (forward iterator). In fact it starts from the last element of the list and moves to the previous one on every call to Iterator.next() method. This solution uses ReverseListIterator class from Apache Commons library.

The last way uses method Lists.reverse() from Guava library to create a reversed view of the given list. Once we have the view, which is in fact instance of Iterable interface, we can go over it using enhanced loop.

As usual you can find the source code of this example at GitHub.

Posted in Guava, Java | Tagged , | 1 Comment

Usage of serialVersionUID in Java serialization

Java run-time associates with each serializable class a special version number known as serialVersionUID. This number is later used during deserialization process to verify if the application which performed the serialization and the application which is doing the deserialization have loaded and use compatible Java classes (with respect to serialization). If the receiver has loaded a class which has the same name but different serialVersionUID, the Java run-time will report a problem by throwing an exception of type InvalidClassException.

Generally speaking this kind of mechanism allows us to detect and report any compatibility problems during deserialization process as early as possible. If this check was missing, the deserialization process would likely finish without raising any exception but the created object would contain invalid data. This can easily lead to unpredictable behavior and can later cause errors which root cause is hard to track.

Defining serialVersionUID

A serializable class can specify its own serialVersionUID value explicitly by declaring a static, final field of name serialVersionUID and of type long:

private static final long serialVersionUID = 4038305141805077716L;

The access modifier does not really matter but private is commonly used.

If serial version is not explicitly declared, it will be automatically generated by compiler and stored in the class file. The compiler calculates default serialVersionUID value based on several aspects of the class like its name, its all public and protected members and so on.

The default serialVersionUID calculation may vary between compilers so it may give different results in different environments. To ensure a consistent serialVersionUID value, it is strongly recommended to explicitly declare its value.

Many IDE for Java have a feature which detects if the class should declare serialVersionUID and can report it as a warning. In case of the Eclipse the warning is following:

The serializable class Person does not declare a static final serialVersionUID field of type long

Adding serialVersionUID to existing classes

If you create a new class, you can choose any arbitrary value for its serialVersionUID because there are no serialized objects of this class yet stored anywhere.

However, if you already have an old class to which you would like to add explicit serial version value, you have be careful. The best idea is to choose a value which would be automatically generated by the compiler.

Oracle JDK provides a program serialver which shows the following basic window when started with -show option from command line:
serialver
When you enter a full name of the existing class and click Show button, it will output its default generated serialVersionUID below. You can just copy-paste this line into Java class file.

Additionally, Eclipse provides a nice context tool which can simplify adding serialVersionUID:

serialver-eclipse

Managing explicit serialVersionUID

The serialVersionUID represents a class version and has to be managed. If you make a change to the existing class which makes it incompatible with the previous version (e.g. changing base class, modifying the types of the fields or removing fields), it is also necessary to modify (e.g. increment) its serialVersionUID value.

On the other hand, adding new fields to the class does not necessarily make it incompatible. Additionally, you may manage to keep the class backward compatible by customizing serialization procedure using readObject() and writeObject() methods.

Example

Let’s consider a simple application serializing and deserializing objects of Person class:

package com.example.serialversionuid;

import java.io.Serializable;

public class Person implements Serializable {
    private static final long serialVersionUID = 4038305141805077716L;
	
    private String firstName;
    private String lastName;

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return "Person{" + "firstName=" + firstName + ", lastName=" + lastName + '}';
    }
    
}

with the main class:

package com.example.serialversionuid;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Main {
    
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        if (args.length != 2) {
            System.err.println("Usage: serialversionuid read|write FILE");
            return;
        }
        final String operation = args[0];
        final String file = args[1];
        
        
        if (operation.equals("read")) {
            try (ObjectInputStream is = new ObjectInputStream(new FileInputStream(file))) {
                Person person = (Person) is.readObject();
                System.out.println("Read person: " + person);
            }
        } else if (operation.equals("write")) {
            Person person = new Person("James", "Bond");
            try (ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(file))) {
                os.writeObject(person);
            }
        }
    }
    
}

When the serialVersionUID is unchanged we can serialize and deserialize objects of Person class without problems:

$ java -jar serialversionuid-1.0-SNAPSHOT.jar write data.ser
$ java -jar serialversionuid-1.0-SNAPSHOT.jar read data.ser
Read person: Person{firstName=James, lastName=Bond}

However, if you change serialVersionUID in Person class, rebuild the jar file and try to read data.ser file again, the InvalidClassException exception appears:

$ java -jar serialversionuid-1.0-SNAPSHOT.jar read data.ser
Exception in thread "main" java.io.InvalidClassException: com.example.serialversionuid.Person; local class incompatible: stream classdesc serialVersionUID = 4038305141805077716, local class serialVersionUID = 4038305141805077717
        at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:617)
        at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1622)
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
        at com.example.serialversionuid.Main.main(Main.java:22)

As you can easily notice, the Java run-time reports incompatibility between classes used to serialize data and deserialize it later.

Conclusion

Field serialVersionUID is often forgotten and ignored. In most cases not declaring it does not cause any problems. However, if your application exchanges serialized objects over network with another applications or stores serialized data on disk for long time, the importance of proper management of serialVersionUID becomes crucial.

The source code for the example is available at GitHub.

Posted in Java | Tagged | Leave a comment

Creating executable jar file with Maven

Building an executable jar file with maven-jar-plugin is fairly easy. However, it has one disadvantage that the Maven dependencies are not packaged together inside the resulting jar file but have to be stored separately on the file system and added to the class path. Often it is not an issue but sometimes you may want to create a single self-contained jar file which does not depend on anything external. Below you can find an example how to do it.

Consider we have a very simple application with main method:

package com.example.runnablejar;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.codec.digest.DigestUtils;

public class Main {
    
    public static void main(String[] args) throws IOException  {
        if (args.length == 0) {
            System.err.println("Usage: cryptohash file1 ...");
            return;
        }
        
        for (String arg : args) {
            Path file = Paths.get(arg);
            calculateSha256CommonsIO2(file);
        }
    }
        
    private static void calculateSha256CommonsIO2(Path path) throws IOException {
        try (InputStream is = Files.newInputStream(path)) {
            String hashString = DigestUtils.sha256Hex(is);
            System.out.printf("SHA256(%s) = %s%n", path, hashString);
        }
    }

}

This application uses Apache Commons Codec library which is distributed as a separate jar file. Storing this jar file inside our application jar file would not work because it would not be present on the class path.

To overcome this issue we can use maven-assembly-plugin in POM file:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>runnablejar</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>runnablejar</name>

    <build>
        <plugins>      
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>           
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.5.2</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                        <configuration>
                            <appendAssemblyId>false</appendAssemblyId>
                            <descriptorRefs>
                                <descriptorRef>jar-with-dependencies</descriptorRef>
                            </descriptorRefs>
                            <archive>
                                <manifest>
                                    <mainClass>com.example.runnablejar.Main</mainClass>
                                </manifest>
                            </archive>
                        </configuration>
                    </execution>
                </executions>
            </plugin>        
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.9</version>
            <type>jar</type>
        </dependency>
    </dependencies>
</project>

Looking from the high level perspective the Apache Commons Codec jar will be merged together with our application jar and the new MANIFEST.MF file will be added:

$ jar tf runnablejar-1.0-SNAPSHOT.jar 
META-INF/
META-INF/MANIFEST.MF
(...)
org/apache/commons/codec/digest/DigestUtils.class
(...)
com/
com/example/
com/example/runnablejar/
com/example/runnablejar/Main.class
(...)

The final application can be run using following command:

$ java -jar runnablejar-1.0-SNAPSHOT.jar

without manually downloading any dependencies and without configuring class path.

As usual you can find the source code of the example at GitHub.

Posted in Java, Maven | Tagged , | 3 Comments

Customizing HTML file input style

Styling an HTML file input control is very cumbersome and the number of available options is quite limited. Moreover, each web browser renders this control differently which makes it difficult to have consistent view of the application between browsers. Here is how the HTML file input looks in Mozilla Firefox:
file-input-img0

In most cases it is impossible to style the HTML file input control with CSS to obtain the wanted results. One of the easiest ways to overcome this issue is to place the HTML file input control visually on top of the other element (e.g. image or button) and make the HTML file input control transparent by setting opacity to zero. Because the HTML file input is on top, it will receive the click event and will open the dialog for choosing the file.

No JavaScript code is necessary, only HTML:

<div class="file-input-container">
   <div>Upload</div>
   <input id="file-input1" type="file"></input>
</div>

and a bit of CSS:

.file-input-container {
  position: relative;
  width: 10em;
  height: 2em;
 
  background: #8ac007;
  border: 2px solid black;
}

.file-input-container > div {
  text-align: center;
  margin-top: 0.5em;
}

.file-input-container > input {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
   
  filter: alpha(opacity=0);
  opacity: 0;
}

After the change the button for choosing the file will look more or less like this:
file-input-img1

The provided example is easy to tune. For example you can completely remove child div element and set the background CSS property on parent div.

Posted in HTML | Tagged , | Leave a comment

Primary key generation in JPA

Generation of primary key values is a very important functionality of relational database management systems. The main idea is to let RDBMS automatically calculate and assign primary key value to the row being inserted into the database table. This not only simplifies the source code of the application using database but also makes the application more robust. There are two widely known methods to perform the generation of primary key values on the database level: auto-increment/identity columns or sequences.

Due to these differences JPA provide three different ways to automatically generate primary key values. Let’s explore them below.

Using auto-increment/identity columns

MySQL and Microsoft SQL Server provide a functionality to automatically generate a unique number for the primary key when a new row is added into a table. For example, in MySQL it is done by marking primary key column with AUTO_INCREMENT keyword:

CREATE TABLE JPAGEN_ADDRESS
(
  ID INT PRIMARY KEY AUTO_INCREMENT,
  CITY VARCHAR(255) NOT NULL,
  STREET VARCHAR(255) NOT NULL
);

and in Microsoft SQL Server with IDENTITY keyword:

CREATE TABLE JPAGEN_ADDRESS
(
    ID INT IDENTITY(1,1) PRIMARY KEY,
    CITY VARCHAR(255) NOT NULL,
    STREET VARCHAR(255) NOT NULL
);

JPA must be informed to use auto-increment/identity for primary key by specifying IDENTITY generation strategy on the ID column:

@Entity
@Table(name = "JPAGEN_ADDRESS")
public class Address implements Serializable {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    // other fields and methods are omitted 
}

Now, when a new Address entity is persisted, the value of the primary key column will be automatically generated by the database.

Using sequences

Oracle Database and PostgreSQL use explicit sequence type to generate unique primary key numbers. For example, in Oracle Database the table and its sequence can be defined like this:

CREATE TABLE JPAGEN_ADDRESS
(
    ID NUMBER PRIMARY KEY,
    CITY VARCHAR(255) NOT NULL,
    STREET VARCHAR(255) NOT NULL
);
CREATE SEQUENCE JPAGEN_ADDRESS_SEQ START WITH 100;

When using sequence for given entity, the SEQUENCE strategy must be defined on its ID column and additionally the name of the sequence must be specified using @SequenceGenerator annotation:

@Entity
@Table(name = "JPAGEN_ADDRESS")
public class Address implements Serializable {
    
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
            generator = "addressGen")
    @SequenceGenerator(name = "addressGen",
            sequenceName = "JPAGEN_ADDRESS_SEQ")
    private long id;

    // other fields and methods are omitted 
}

When a new Address entity is persisted, JPA implementation will obtain the next value of the sequence and use it to insert a new row into the database table.

Using table

The third method to generate primary keys is to have a separate table which stores in a single row the sequence name along with the next number to use for the primary key. For performance reasons the next value is not increased by one, whenever JPA implementation needs a new value for the primary key but by a much higher number (e.g. 50). Once the range of values becomes reserved, JPA implementation can assign primary keys without accessing the database. When every value in the range becomes used, JPA implementation reserves a new range and the cycle continues.

Additionally, in such table we can have multiple rows with each row serving different entity. The only requirement is to use unique sequence names for each entity.

The table strategy is the most complicated one but it is the only strategy that is really portable across different databases. If you are developing an application which can use multiple RDBMS or there may be a need to port to a new RDBMS in the future, using table strategy is the most viable option.

The table to store the Person entity and the table to generate sequences can be created like this:

CREATE TABLE JPAGEN_PERSON
(
    ID NUMBER PRIMARY KEY,
    NAME VARCHAR(255) NOT NULL,
    ADDRESS_ID NUMBER
);
CREATE TABLE JPAGEN_GENERATORS
(
    NAME VARCHAR(255) PRIMARY KEY,
    VALUE NUMBER
);

The table with sequences must have two columns. The first one should contain the name of the sequence and the second one should be of numeric type. JPA implementation will automatically update the second column when reserving a new range of values.

Additionally, JPA must be instructed to use TABLE generation strategy:

@Entity
@Table(name = "JPAGEN_PERSON")
public class Person implements Serializable {
    
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE,
            generator = "personGen")
    @TableGenerator(name = "personGen",
            table = "JPAGEN_GENERATORS",
            pkColumnName = "NAME",
            pkColumnValue = "JPAGEN_PERSON_GEN",
            valueColumnName = "VALUE")
    private long id;

    // other fields and methods are omitted 
}

We also have to add @TableGenerator annotation to inform JPA about the name of the generator table (table element), the names of its both columns (pkColumnName and valueColumnName elements) and also the name of the sequence (pkColumnValue element).

Conclusion

The choice between IDENTITY, SEQUENCE and TABLE strategies should be pretty obvious. If you application is going to use only one RDBMS, the first or the second option should be fine (depending on your RDBMS vendor). In other cases, TABLE strategy is preferred, if not the only possible.

The source code for the article can be found at GitHub. It has been tested with Oracle Database 11gR2 so it supports only SEQUENCE and TABLE strategies.

Posted in Database, Java, Java EE, JPA | Tagged , , , | 1 Comment

Charts with jqPlot, Spring REST, AJAX and JQuery

One of the most notable features of HTML5 is Canvas API which provides a rectangular region for custom drawing. There are several uses of it like: building games, image compositions, animations but this time we will see how to use it to render charts in web browser. Of course, we could write source code for drawing charts ourselves but it would be tedious and time-consuming task. Fortunately, there are several good JavaScript libraries which can perform this task for us.

One of them is jqPlot which we will use to render a very simple chart showing sales in given country in the current month:

springrestchart

Creating web page

Our only web page sales.jsp is pretty simple:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="s" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Sales</title>
        <script type="text/javascript"
                src="<s:url value='/resources/libs/jquery/jquery.min.js' />" >
        </script>
        <script type="text/javascript"
                src="<s:url value='/resources/libs/jqplot/jquery.jqplot.min.js' />" >
        </script>
        <link href="<s:url value='/resources/libs/jqplot/jquery.jqplot.min.css'/>"
              rel="stylesheet" type="text/css" />

        <script type="text/javascript"
                src="<s:url value='/resources/js/sales.js' />" >
        </script>
        <link href="<s:url value='/resources/css/styles.css'/>"
              rel="stylesheet" type="text/css" />
    </head>
    <body>
        <form>
            <label for="countrySelect">Country:</label>
            <select id="countrySelect" >
                <c:forEach var="country" items="${countries}">
                    <option value="${country.code}">${country.name}</option>
                </c:forEach>
            </select>
        </form>

        <div id="chartError">Failed to contact server</div>
        <div id="chart"></div>
    </body>
</html>

First, we include JQuery and jqPlot libraries which we are going to use later. Then we add our custom js/sales.js JavaScript file which contains logic to asynchronously update the web page once the country is changed (we will look at this file later) and css/styles.css CSS stylesheet:

#countrySelect {
    padding: 0.2em;
}

#chart {
    margin: 1em;
    width: 90%;
    height: auto;
    clear: both;
}

#chartError {
    margin: 1em;
    border-style: solid;
    border-width: medium;
    border-color: black;
    background-color: lightpink;
    font-weight: bold;
    padding: 0.5em;
    display: none;
}

Finally, we add a drop-down menu with the list of all supported countries, a message to inform user about error (hidden by default) and a div element with identifier chart in which jqPlot will draw the chart. There are no canvas elements at the moment but they will be added later to the DOM tree by jqPlot.

Creating Spring MVC controller

The JSP web page retrieves the list of all supported countries from ${countries} model attribute which was populated by the following Spring controller:

package com.example.springrestchart;

import java.util.List;
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;

@Controller
@RequestMapping(value = "/sales")
public class SalesController {
    
    @Autowired
    private SalesProvider salesProvider;
    
    @RequestMapping(method = RequestMethod.GET)
    public String list(Model model) {
        
        List<Country> countries = salesProvider.getCountries();
        model.addAttribute("countries", countries);
        
        return "sales";
    }
}

The controller uses injected instance of SalesProvider singleton bean:

package com.example.springrestchart;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;

@Component
public class SalesProvider {

    private static final List<Country> COUNTRIES = Arrays.asList(
            new Country("UK", "United Kingdom"),
            new Country("DE", "Germany"),
            new Country("FR", "France"));

    private Map<String, Sales> sales;

    @PostConstruct
    protected void init() {
        sales = createSales();
    }

    public List<Country> getCountries() {
        return Collections.unmodifiableList(COUNTRIES);
    }

    public Sales getSales(String countryCode) {
        Sales result = sales.get(countryCode);
        if (result != null) {
            return result;
        } else {
            throw new CountryNotFoundException("Country not found: " + countryCode);
        }
    }

    private static Map<String, Sales> createSales() {
       // omitted
    }

    private static Sales createRandomValues(Country country, int count) {
       // omitted
    }

}

This provider is responsible for returning the list of all supported countries and their sales from the first day of the current month till today (one value per day). The actual sales values are auto-generated but the source code for this was omitted for clarity.

One important thing to notice is that getSales() method throws CountryNotFoundException exception if the sales for given country do not exist. The definitions of Country, Sales and CountryNotFoundException classes are straightforward:

Country:

package com.example.springrestchart;

public class Country {
    private String code;
    private String name;

    public Country(String code, String name) {
        this.code = code;
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public String getName() {
        return name;
    }
    
}

Sales:

package com.example.springrestchart;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Sales {
    private Country country;
    private List<Double> values;

    public Sales(Country country, List<Double> values) {
        this.country = country;
        this.values = new ArrayList<>(values);
    }

    public Country getCountry() {
        return country;
    }
    
    public List<Double> getValues() {
        return Collections.unmodifiableList(values);
    }
}

CountryNotFoundException:

package com.example.springrestchart;

public class CountryNotFoundException extends RuntimeException {

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

Creating Spring REST Controller

Now, we should have the web page display in web browser but nothing happens when country is changed. We are going to improve this by exposing the sales data using REST web service and later consuming it from web browser using AJAX and JQuery.

Let’s first look at the implementation of REST web service:

package com.example.springrestchart;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
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.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

@Controller
public class SalesRestController {
    
    @Autowired
    private SalesProvider salesProvider;
    
    @RequestMapping(value = "/services/sales/{countryCode}", method = RequestMethod.GET,
            produces = "application/json")
    public @ResponseBody Sales getSales(@PathVariable("countryCode") String countryCode) {
        return salesProvider.getSales(countryCode);
    }
    
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(CountryNotFoundException.class)
    public void countryNotFound() {
    }
}

It is a standard Spring controller with @Controller annotation. Alternatively, we could use RestController annotation and remove @ResponseBody annotation from getSales() method.

We inject the same SalesProvider as before to access sales data. Method getSales() of the controller is annotated with @RequestMapping to inform Spring that it should be invoked whenever HTTP GET request is sent to relative and parameterized URL services/sales/{countryCode} where {countryCode} should be replaced by one of the available country codes. In our case it would be UK, DE or FR.

Finally, we add @ResponseBody annotation to the method and specify produces element to indicate that the returned instance of Sales class should be automatically converted to JSON format before sending over HTTP. Spring uses Jackson library for this task so it must be added as a dependency to Maven POM file:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.4.1.1</version>
</dependency>

If you have been following the article closely, you should remember that getSales() method in SalesProvider class throws CountryNotFoundException if the country code is unknown. Therefore, we install exception handler for it and inform Spring to return HTTP status 404 (Not Found) in case of the exception.

Implementing client-side logic

Finally, after all setup is done we can take a look at previously mentioned js/sales.js file:


function renderPlot(data) {
    $("#chart").empty();
    $("#chartError").hide();

    var countryName = data.country.name;
    var values = data.values;
    $.jqplot("chart", [values], {
        title: "<h2> Sales in " + countryName + " in current month</h2>",
        axes: {
            xaxis: {
                label: 'Day of month',
                pad: 0
            },
            yaxis: {
                label: 'Mln USD'
            }
        }});
}

function renderError() {
    $("#chart").empty();
    $("#chartError").show();
}

function reloadPlot(countryCode) {
    $.ajax({
        url: encodeURI("services/sales/" + countryCode),
        type: "GET",
        dataType: "json",
        success: renderPlot,
        error: renderError
    });
}

function initializeCountries() {
    $("#countrySelect").change(function() {
        var countryCode = $(this).val();
        reloadPlot(countryCode);
    });
    reloadPlot($("#countrySelect").val());
}

$(document).ready(initializeCountries);

Function initializeCountries(), which is called once the document is fully loaded, registers listener to be called when the country is changed. If it happens, the plot is reloaded to show the data for the chosen country.

Function reloadPlot() issues AJAX request to our REST service and passes country code as part of the URL path. If the request succeeds, renderPlot() function is called to render the chart with sales. Because the sales data was returned in JSON format, we can easily access it from JavaScript. We extract the country name and its sales (as an array of real numbers).

Finally, we call $.jqplot() function and pass it identifier of the div element where the chart should be rendered and the actual data. jqPlot can draw multiple data series on a single chart. We have only one series so we have to create an array with one element (our array with sales data) beforehand. jqPlot can take multiple options to customize appearance and behavior of the chart but for simplicity we specify axes labels and padding only.

Conclusion

jqPlot is versatile and very easy to use JavaScript library to render charts in web browser. The functionality used in this article is only a small portion of what it can do.

From user point of view charts generated by such library are more responsive and interactive than the charts generated on a server. It is for example possible to use highlighting, zooming, cursor tracking and even modify the values using drag-and-drop.

The complete source code for the example is available at GitHub.

Posted in AJAX, Java, Spring | Tagged , , , , , , | Leave a comment