Book Image

Java EE 8 and Angular

By : Prashant Padmanabhan
Book Image

Java EE 8 and Angular

By: Prashant Padmanabhan

Overview of this book

The demand for modern and high performing web enterprise applications is growing rapidly. No more is a basic HTML frontend enough to meet customer demands. This book will be your one-stop guide to build outstanding enterprise web applications with Java EE and Angular. It will teach you how to harness the power of Java EE to build sturdy backends while applying Angular on the frontend. Your journey to building modern web enterprise applications starts here! The book starts with a brief introduction to the fundamentals of Java EE and all the new APIs offered in the latest release. Armed with the knowledge of Java EE 8, you will go over what it's like to build an end-to-end application, configure database connection for JPA, and build scalable microservices using RESTful APIs running in Docker containers. Taking advantage of the Payara Micro capabilities, you will build an Issue Management System, which will have various features exposed as services using the Java EE backend. With a detailed coverage of Angular fundamentals, the book will expand the Issue Management System by building a modern single page application frontend. Moving forward, you will learn to fit both the pieces together, that is, the frontend Angular application with the backend Java EE microservices. As each unit in a microservice promotes high cohesion, you will learn different ways in which independent units can be tested efficiently. Finishing off with concepts on securing your enterprise applications, this book is a handson guide for building modern web applications.
Table of Contents (16 chapters)

Overview of Java SE 8

One of the goals of Java EE 8 was better alignment with Java SE 8. SE 8 was a major update; it was released in March 2014 and brought with it some major changes to the language and APIs. Lambdas, streams, default methods, and functional-style programming were introduced and were the highlights of the release. With these capabilities, the method of writing code was no longer going to be the same. A few other noteworthy additions in this release were optionals, repeating annotations, the date/time APIs, type annotations, and CompletableFutures.

If you would like to dig deeper into this release, then considering reading a book specific to Java 8. Here, we will cover just enough for getting to grips with some of the language features.

Lambdas, streams, and default methods

Lambdas have been the biggest change in the language since generics were introduced in Java 5. This was a fundamental change that impacted many of the APIs to follow. Anonymous classes are very useful to pass code around, but they come at the cost of readability as they lead to some boilerplate code (think Runnable or ActionListener). Those wanting to write clean code that is readable and void of any boilerplate would appreciate what lambda expressions have to offer.

In general, lambda expressions can only be used where they will be assigned to a variable whose type is a functional interface. The arrow token (->) is called the lambda operator. A functional interface is simply an interface having exactly one abstract method:

Runnable run = new Runnable() {
@Override
public void run() {
System.out.println("anonymous inner class method");
}
};

With lambdas similar to those in the preceding code, the code can be rewritten as follows, where the empty parenthesis is used for the no args method:

Runnable runWithLambda = () -> System.out.println("hello lambda");

To understand some of the enhancements, let us look at an example. Consider the Hero class, which is a plain Java object with two properties, telling us the name of the Hero and whether the hero can fly or not. Well, yes there are a few who can't fly, so let's keep the flag around:

 class Hero {  
String name;
boolean canFly;

Hero(String name, boolean canFly) {
this.name = name;
this.canFly = canFly;
}
// Getters & Setters omitted for brevity
}

Now, it's typical to see code that iterates over a collection and does some processing with each element in the collection. Most of the methods would typically repeat the code for iterating over a list, but what varies is usually the condition and the processing logic. Imagine if you had to find all heroes who could fly and find all heroes whose name ends with man. You would probably end up with two methods—one for finding flying heroes and another for the name-based filter. Both these methods would have the looping code repeated in them, which would not be that bad, but we could do better. A solution is to use anonymous inner class blocks to solve this, but then it becomes too verbose and obscures the code readability. Since we are talking about lambdas, then you must have guessed by now what solution we can use. The following sample iterates over our Hero list, filtering the elements by some criteria and then processing the matching ones:

List<String> getNamesMeetingCondition(List<Hero> heroList,
Predicate<Hero> condition) {
List<String> foundNames = new ArrayList<>();
for (Hero hero : heroList) {
if (condition.test(hero)) {
foundNames.add(hero.name);
}
}
return foundNames;
}

Here, Predicate<T> is a functional interface new to Java 8; it has one abstract method called test, which returns a Boolean. So, you can assign a lambda expression to the Predicate type. We just made the condition a behavior that can be passed dynamically.

Given a list of heroes, our code can now take advantage of lambdas without having to write the verbose, anonymous inner classes:

List<Hero> heroes = Arrays.asList(
new Hero("Hulk", false),
new Hero("Superman", true),
new Hero("Batman", false));

List<String> result = getNamesMeetingCondition(heroes, h -> h.canFly);
result = getNamesMeetingCondition(heroes, h -> h.name.contains("man"));

And finally, we could print the hero names using the new forEach method available for all collection types:

result.forEach( s -> System.out.println(s));

Moving onto streams, these are a new addition along with core collection library changes. The Stream interface comes with many methods that are helpful in dealing with stream processing. You should try to familiarize yourself with a few of these. To establish the value of streams, let's solve the earlier flow using streams. Taking our earlier example of the hero list, let's say we wanted to filter the heroes by the ability to fly and output the filtered hero names. Here's how its done in the stream world of Java:

heroes.stream().filter(h -> h.canFly)
.map( h -> h.name)
.forEach(s -> System.out.println(s));

The preceding code is using the filter method, which takes a Predicate and then maps each element in the collection to another type. Both filter and map return a stream, and you can use them multiple times to operate on that stream. In our case, we map the filtered Hero objects to the String type, and then finally we use the forEach method to output the names. Note that forEach doesn't return a stream and thus is also considered a terminal method.

If you hadn't noticed earlier, then look again at the previous examples in which we already made use of default methods. Yes, we have been using the forEach method on a collection which accepts a lambda expression. But how did they add this method without breaking existing implementations? Well, it's now possible to add new methods to existing interfaces by means of providing a default method with its own body. For collection types, this method has been defined in the Iterable interface.

These capabilities of Java 8 are now powering many of the EE 8 APIs. For example, the Bean Validation 2.0 release is now more aligned to language constructs such as repeatable annotations, date and time APIs, and optionals. This allows for using annotations to validate both the input and output of various APIs. We will learn more about this as we explore the APIs throughout the book.