9. [TD]: Implementation of the [ui] layer with a console program
Keywords: multi-layer architecture, Spring, dependency injection.
![]() |
9.1. Support
![]() |
In [1], the [support / chap-09] folder contains the Eclipse project for the [UI] layer of the console application.
9.2. Maven Configuration
The Eclipse project [elections-ui-metier-dao-jdbc] is configured by the following Maven [pom.xml] 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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.elections</groupId>
<artifactId>elections-ui-metier-dao-jdbc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>elections-ui-metier-dao-jdbc</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.7.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- business logic -->
<dependency>
<groupId>istia.st.elections</groupId>
<artifactId>elections-business-dao-jdbc</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- plugins -->
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>config.AppConfig</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<!-- for installing the project artifact in the local Maven repository -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- lines 22–26: we import the [business] layer archive and, by extension, the [DAO] layer archive;
9.3. Spring Configuration
![]() |
The [UiConfig] class configures the Spring application:
package elections.ui.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import elections.business.config.BusinessConfig;
@Import(MetierConfig.class)
@ComponentScan(basePackages = { "elections.ui.service" })
public class UiConfig {
}
- Line 8: We import the beans defined in the [business] layer. This layer already imported the beans defined in the [DAO] layer. We therefore have access here to the beans from all three layers;
- line 9: the [elections.ui.service] package contains other beans;
9.4. The [UI] layer interface
![]() |
To understand what the Java interface of the [UI] layer might look like, we need to know who will use this interface. It is not the user from the diagram above, but the program that will launch the application as a whole.
![]() |
The [IElectionsUI] interface is presented to the main program [main], which will launch the application. What can [main] ask of the [UI] layer? It can ask it to initiate interactions with the user who will enter the missing election data. We will adopt the following minimal interface:
package istia.st.elections.ui;
public interface IElectionsUI {
/**
* initiates the dialogue with the user
*/
public void run();
}
- Line 7: The interface has only one method: run. By calling this method, we instruct the [ui] layer to begin interacting with the user.
9.5. The application's entry point
![]() |
The application's startup class is a Java class with a static [main] method. This method must create instances of the [ui, business, demo] layers and instruct the [ui] layer to begin interacting with the user. This class could be the following [AbstractBootElections] class:
package elections.ui.boot;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import elections.dao.entities.ElectionsException;
import elections.ui.config.UiConfig;
import elections.ui.service.IElectionsUI;
public abstract class AbstractBootElections {
// Retrieve the Spring context
protected AnnotationConfigApplicationContext ctx;
public void run() {
// instantiate the [ui] layer
IElectionsUI electionsUI = null;
try {
// retrieve the Spring context
ctx = new AnnotationConfigApplicationContext(UiConfig.class);
// retrieve the [ui] layer
electionsUI = getUI();
} catch (RuntimeException ex) {
// report the error
displayExceptions("The following errors occurred:", ex);
// terminate the application
System.exit(1);
}
// execute [ui] layer
try {
electionsUI.run();
} catch (ElectionsException ex2) {
// report the error
throwExceptions("The following errors occurred:", ex2);
// terminate the application
System.exit(3);
} catch (RuntimeException ex1) {
// report the error
displayExceptions("The following errors occurred:", ex1);
// terminate the application
System.exit(2);
}
}
protected abstract IElectionsUI getUI();
private void displayExceptions(String message, ElectionsException ex) {
// display the message
System.out.println(String.format("%s -------------", message));
System.out.println(String.format("Error code: %d", ex.getCode()));
// display the errors
for (String error : ex.getErrors()) {
System.out.println(String.format("-- %s", error));
}
}
public void displayExceptions(String message, Exception ex) {
// display the message
System.out.println(String.format("%s -------------", message));
// display the exception stack trace
Throwable cause = ex;
while (cause != null) {
System.out.println(String.format("-- %s", cause.getMessage()));
cause = cause.getCause();
}
}
}
- Line 19: The Spring context is instantiated: all beans defined in the various configuration files will be created;
- line 21: we retrieve a reference to the bean that implements the [IElectionsUI] interface. We will implement the [IElectionsUI] interface using two beans:
- [ElectionsConsole] for a console application;
- [ElectionsSwing] for a Swing application;
The [getUI] method is abstract (line 44). In fact, the [AbstractBootElections] class will be the parent of two classes:
- [BootElectionsConsole], which will provide the [ElectionsConsole] bean to its parent class;
- [BootElectionsSwing], which will provide the [ElectionsSwing] bean to its parent class;
- line 30: the [run] method of the interface is executed;
Exceptions are handled in various places:
- lines 22–27: an exception may occur during the instantiation of the Spring context;
- lines 31–41: an exception may occur during the execution of the [UI] layer. There are two types of exceptions:
- the [ElectionsException] class thrown by the [DAO] layer;
- the [RuntimeException] class for other exceptions that may occur during execution;
The [BootElectionsConsole] class is the class that launches the console implementation. Its code is as follows:
package elections.ui.boot;
import elections.ui.service.IElectionsUI;
public class BootElectionsConsole extends AbstractBootElections{
public static void main(String[] arguments) {
new BootElectionsConsole().run();
}
@Override
protected IElectionsUI getUI() {
return ctx.getBean("electionsConsole", IElectionsUI.class);
}
}
- Line 5: The [BootElectionsConsole] class extends the [AbstractBootElections] class. It must therefore implement the [getUI] method that its parent class had declared as abstract;
- lines 10–13: implementation of the [getUI] method;
- line 12: the bean named [electionsConsole] is made to implement the [IElectionsUI] interface. [ctx] is the Spring context defined in the parent class by the declaration:
// retrieve the Spring context
protected AnnotationConfigApplicationContext ctx;
Because the field has the [protected] attribute, it is visible in child classes.
The bean on line 12 will be declared as follows:
@Component
public class ElectionsConsole implements IElectionsUI {
The default name of this bean is the class name with its first letter lowercase. You can override this default name by explicitly writing:
The [BootElectionsSwing] class that would launch the Swing implementation could be as follows:
package elections.ui.boot;
import elections.ui.service.IElectionsUI;
public class BootElectionsSwing extends AbstractBootElections {
public static void main(String[] arguments) {
new BootElectionsSwing().run();
}
@Override
protected IElectionsUI getUI() {
return ctx.getBean("electionsSwing", IElectionsUI.class);
}
}
This design pattern, in which behavior common to classes is factored into a parent class, and child classes are left to implement their specific details, is called the Strategy design pattern. This design pattern imposes a common behavior on all child classes.
9.6. The implementation class [ElectionsConsole]
![]() |
Our first implementation class for the [ui] layer will be a class that uses the console to communicate with the user. Here is an example of a dialog displayed on the Eclipse console:
There are 7 competing lists. Please enter the number of votes for each of them:
Number of votes for list [A]: 2500
Number of votes for list [B]: 4500
Number of votes for list [C]: x
Invalid number of votes. Please try again
Number of votes for list [C]: 8,000
Number of votes for list [D]: 12,000
Number of votes for list [E]: 16,000
Number of votes for list [F]: 25,000
Number of votes for list [G]: 32,000
Election results
[G,32,000,2,false]
[F,25,000,2,false]
[E,16,000,1,false]
[D,12,000,1,false]
[C,8000,0,false]
[B,4500,0,true]
[A,2500,0,true]
The [ElectionsConsole] class could have the following skeleton:
package elections.ui.service;
import java.util.Comparator;
import java.util.Scanner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import elections.dao.entities.VoterList;
import elections.business.service.IElectionsBusiness;
@Component
public class ElectionsConsole implements IElectionsUI {
@Autowired
private IElectionsMetier electionsMetier;
@Override
public void run() {
// data entry
try (Scanner keyboard = new Scanner(System.in)) {
// request the competing lists from the [business] layer
// enter votes
}
// calculate the seats
// save the results
// sort the lists in descending order of votes
// display them
}
// class for comparing electoral lists
class CompareElectionLists implements Comparator<ElectionList> {
// Compare two candidate lists based on the number of votes
@Override
public int compare(VoterList voterList1, VoterList voterList2) {
// compare the votes of these two lists
int votes1 = electoralList1.getVotes();
int votes2 = electoralList2.getVotes();
if (votes1 < votes2) {
return +1;
} else {
if (votes1 > votes2)
return -1;
else
return 0;
}
}
}
}
- line 12: the [ElectionsConsole] class is a Spring component;
- line 13: the class implements the [IElectionsUI] interface
- lines 15–16: Spring injects a reference into the [business] layer;
- lines 18–34: the [run] method of the [IelectionsUI] interface;
The try block in lines 21–28 is called try-with-resources, and its syntax is as follows:
The resource in line 1 must implement the [java.lang.AutoCloseable] interface. The resource is opened on line 1 and automatically closed after line 3, regardless of whether an exception occurs in the code executed between the two lines. This syntax ensures that an open resource will be closed, no matter what happens.
Task: Write the code for the [run] method. Use the comments as a guide.






