Close

Enabling HTTPS in your Java server using a free certificate

Using HTTPS connections in web application client-server communication has grown to be more and more common nowadays. In fact, for some types of applications, it’s become a requirement. For example, if you want to use Geolocation API in a web app running in Chrome, you need to have HTTPS enabled. All big browser vendors require HTTPS if you want to use HTTP/2. Even your search rank in Google is higher, if you provide a secure connection. Besides, web app users today are expecting to see that green secure connection indicator in the URL bar. So how does one enable it? Luckily for you as a web app developer, making the web more secure means only a few easy steps, and it’s completely free when using a service called Let’s Encrypt.

What you need before starting

I assume you have a server somewhere and you are running some kind of Java server, such as Jetty or Tomcat. For obtaining and installing the certificate we’ll use a tool called Certbot. Certbot is an ACME (Automatic Certificate Management Environment) protocol client, which fetches a certificate from Let’s Encrypt—an open certificate authority launched by the EFF, Mozilla, and others. Let’s Encrypt is a relatively new CA aiming to lower the complexity of setting up and maintaining TLS (Transport Layer Security). You will need root access in your server to run Certbot.

I’ve tested the steps in this guide using Jetty, but the same steps work at least for Tomcat and WildFly (JBoss). I’ve written the guide to be applicable to practically any Java server. When testing, I used a dashboard web application written in Java using Vaadin Framework and Vaadin Charts.


Example Java web application using the Let's encrypt certificate

Installing Certbot ACME client

You can check if your OS distribution comes packaged with Certbot by visiting https://certbot.eff.org/. From the same address you’ll find more instructions for the specific environment, if you need them. If your OS doesn’t come with certbot, install it using certbot-auto. Certbot-auto is a wrapper script accepting the same control flags as certbot and updating the client code automatically. It requires root access.

wget https://dl.eff.org/certbot-auto
chmod a+x ./certbot-auto
./certbot-auto --help

Obtaining a signed certificate

Certbot has a few plugins that can fetch and install certificates automatically. Unfortunately such a plugin is not available for Java servers. I recommend using either the standalone plugin or the webroot plugin. The standalone plugin starts a lightweight web server and binds it to port 80 or 443 and serves a verification for the Let’s Encrypt validation servers. This means that either port must be free while running the plugin i.e. the Java server must not be running if it uses those ports. To use the standalone plugin, run

certbot-auto certonly --standalone --standalone-supported-challenges http-01 -d yourdomain.com

The --standalone-supported-challenges option value can also be tls-sni-01 to use port 443.

The other choice for the plugin is to use the webroot plugin, which uses an existing running server to serve the ACME protocol verification files. E.g. if your Jetty webapps directory is /opt/jetty/webapps/, run

certbot-auto certonly --webroot -w /opt/jetty/webapps/ROOT -d yourdomain.com

With both webroot plugin and standalone plugin the certonly option certbot will fetch a certificate and store it to /etc/letsencrypt/live/<yourdomain>.

Certificates from Let’s Encrypt are short lived, only 90 days until they expire and must be renewed. Luckily certbot can do this renewal for you with the renew option.

certbot-auto renew

The renew command checks if the certificate actually needs to be updated, so it’s safe to run every day and doesn’t burn your ACME request rate limit. We’ll cover automating the renewal, but first let’s install the certificate to Java keystore.

Installing the certificate to Java keystore

The certificate now needs to be installed to the Java keystore. Before we can install the certificate, we need to combine the private key and the certificate to a PKCS12 format file.

openssl pkcs12 -export -in /etc/letsencrypt/live/yourdomain.com/fullchain.pem -inkey /etc/letsencrypt/live/yourdomain.com/privkey.pem -out /etc/letsenscrypt/live/yourdomain.com/pkcs.p12 -name mytlskeyalias -passout pass:mykeypassword

After the certificate is in PKCS12 format, we remove the previously stored certificate from the keystore. Of course, you need to do this step only if you have previously stored a certificate for your domain e.g. if you have just renewed a certificate and want to store an updated one.

keytool -keystore /path/to/my/keystore -delete -alias ‘mytlskeyalias’ -storepass ‘mystorepassword’

Import the .p12 file to keystore.

keytool -importkeystore -deststorepass mystorepassword -destkeypass mykeypassword -destkeystore /path/to/my/keystore -srckeystore /etc/letsencrypt/live/mydomain.com/pkcs.p12 -srcstoretype PKCS12 -srcstorepass mykeypassword -alias mytlskeyalias

Now the certificate is ready to be used in your Java server. It still needs to be configured though, but let’s automate the certificate renewal first.

Automating the certificate renewal

Obtaining and installing the certificate only once is not enough, as the certificate has an expiration date. Certificates from Let’s Encrypt are quite short lived - only 90 days. The certificates can also be revoked. For the above-mentioned reasons, you should check if your certificates need to be renewed every day. Let’s make a couple of scripts to automate this.

First let’s write a script ‘renew-cert.sh’ for running the validation and renewal of the certificate. You can then put this script into your crontab to be run once a day.

#!/bin/bash
certbot-auto renew --no-self-upgrade --quiet --renew-hook "store-renewed.sh"

The certbot command is the same we used earlier, but now we’ll throw in options --no-self-upgrade to prevent the script updating itself, which is not necessarily wanted in production environment, and --quiet to disable output other than errors. Certbot checks if the certificate needs to be renewed before actually renewing it. We’ll use the --renew-hook option to specify a script or command to be executed in case of the certificate being renewed.

Now, let’s write the script that is run in certbot renew hook. Create a script called ‘store-renewed.sh’ and put in those certificate conversion and keystore commands we used earlier: first pkcs12 conversion, then removing the old key, and finally importing the new key. You can download a sample bash script file and edit it to your own settings.

After updating the certificate in the keystore, you need to restart your Java server. The restart command depends on what server you are using and how you are running it e.g. if you are running Jetty as a service, add service jetty restart to the end of ‘store-renewed.sh’.

Configuring SSL/TLS in your Java server

Putting your certificate in a keystore for the Java server is not enough. You need to configure your server to use the certificate. I’m not going to cover individual server configurations, but here are a few links to how to configure Tomcat 8, Jetty 9 and WildFly 10. After you have configured your Java server to use the certificate, everything should be ready. Good work.

Write your first secure web application with Vaadin—the open source Java web UI framework. Did you know that Vaadin applications are more secure by default?

Read about security in Vaadin

Using Oracle Databases and WebLogic with Vaadin 8

This guide walks you through the process of connecting to Oracle databases from Java EE web applications using JPA and Apache DeltaSpike Data. The application developed in this guide consists of a web UI with a list of employees loaded from an Oracle database and a filter to allow searching employees by their names.

Prerequisites

This guide assumes you have:

  1. Installed Oracle Database. The syntax presented in this guide is compatible with Oracle Database Express Edition 11g Release 2 (11.2) which is a free-to-use entry-level version of Oracle Database 11g.
  2. Installed Java EE 7 server like Oracle WebLogic Server. This guide uses Oracle WebLogic Server 12cR2 (12.2.1.1.0).
  3. Installed your favorite IDE and Maven.

No previous experience with Java EE, JPA or Apache DeltaSpike is required to follow this guide.

Create the database objects

Connect to the Oracle database and create the following table:

CREATE TABLE employee(
  id NUMBER NOT NULL,
  name VARCHAR(255),
  email VARCHAR(255)
);

Create the following sequence which is going to be used as a generator of the values in the id column of the previous table:

CREATE SEQUENCE employee_seq INCREMENT BY 1;

Add some test data, such as the following:

INSERT INTO employee(id, name, email) VALUES(employee_seq.nextval, 'John Volga', 'john@test.com');
INSERT INTO employee(id, name, email) VALUES(employee_seq.nextval, 'Matti Tahvonen', 'matti@test.com');
INSERT INTO employee(id, name, email) VALUES(employee_seq.nextval, 'Sami Ekblad', 'sami@test.com');
INSERT INTO employee(id, name, email) VALUES(employee_seq.nextval, 'Nicolas Frankel', 'nicolas@test.com');
INSERT INTO employee(id, name, email) VALUES(employee_seq.nextval, 'Alejandro Duarte', 'alejandro@test.com');

Remember to commit the changes to the database if necessary.

Create a new DataSource

If you haven’t, create a new WebLogic domain (make sure you add an Administration Server when creating the domain and take note of the path to the created domain). Start the WebLogic server, login into the Administration Console (available at http://localhost:7001/console by default), and add a new generic JDBC DataSource. Make sure to use the following configuration when creating the DataSource:

  • Name: DemoDS

  • Scope: Global

  • JNDI Name: demoDS

  • Database Type: Oracle

Configure the database name, hostname, port, database user name, and password to connect to your Oracle Database Server instance. You don’t need to specify any additional connection properties.

Make sure you select a target to deploy the new DataSource (AdminServer, for example) in the last step of the DataSource creation wizard.

Install the WebLogic Maven Plugin

The WebLogic Maven Plugin allows you to perform several operations such as starting the WebLogic server and deploying applications to it. In order to use the plugin, you need to install it into your local Maven repository first. Execute the following commands in a terminal:

cd ORACLE_HOME/oracle_common/plugins/maven/com/oracle/maven/oracle-maven-sync/12.2.1

mvn install:install-file -DpomFile=oracle-maven-sync-12.2.1.pom -Dfile=oracle-maven-sync-12.2.1.jar

mvn com.oracle.maven:oracle-maven-sync:push -DoracleHome=ORACLE_HOME

Replace ORACLE_HOME with the full path of the directory where you installed WebLogic and HOME with the full path of your home directory.

Create a Java EE application using Maven

An easy way to create a new Java EE application is by using the webapp-javaee7 Maven archetype. Execute the following command to create a new Java EE application:

mvn -B archetype:generate -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=webapp-javaee7 -DarchetypeVersion=1.1 -DgroupId=com.example -DartifactId=demo -Dversion=1.0-SNAPSHOT

You should get a Maven project you can import into your favorite IDE.

Add the following into the <build> section of the pom.xml file:

<finalName>demo</finalName>

This configures the name of the generated WAR file to demo.war instead of something like demo-1.0-SNAPSHOT.war. This makes the web application available at localhost:7001/demo without any version information in the URI.

Add the WebLogic Maven Plugin

Add the following into the <plugins> section of the pom.xml file:

<plugin>
    <groupId>com.oracle.weblogic</groupId>
    <artifactId>weblogic-maven-plugin</artifactId>
    <version>12.2.1-1-0</version>
    <configuration>
        <domainHome>PATH_TO_YOUR_DOMAIN_DIRECTORY</domainHome>
        <user>YOUR_WEBLOGIC_USER</user>
        <password>YOUR_WEBLOGIC_PASSWORD</password>
    </configuration>
</plugin>

Change PATH_TO_YOUR_DOMAIN_DIRECTORY with the full path of the WebLogic domain you previously created. Also change YOUR_WEBLOGIC_USER and YOUR_WEBLOGIC_PASSWORD with the actual values required to connect to your WebLogic instance.

Add the required dependencies

Add the Apache DeltaSpike Data and Vaadin dependencies in the pom.xml file:

<repositories>
    <repository>
        <id>vaadin-addons</id>
        <url>http://maven.vaadin.com/vaadin-addons</url>
    </repository>
    <repository>
        <id>vaadin-prereleases</id>
        <url>http://maven.vaadin.com/vaadin-prereleases</url>
    </repository>
</repositories>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.deltaspike.distribution</groupId>
            <artifactId>distributions-bom</artifactId>
            <version>1.7.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-bom</artifactId>
            <version>8.0.0.beta1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-web-api</artifactId>
        <version>7.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.deltaspike.modules</groupId>
        <artifactId>deltaspike-data-module-api</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.deltaspike.modules</groupId>
        <artifactId>deltaspike-data-module-impl</artifactId>
    </dependency>
    <dependency>
        <groupId>com.vaadin</groupId>
        <artifactId>vaadin-server</artifactId>
    </dependency>
    <dependency>
        <groupId>com.vaadin</groupId>
        <artifactId>vaadin-client-compiled</artifactId>
    </dependency>
    <dependency>
        <groupId>com.vaadin</groupId>
        <artifactId>vaadin-themes</artifactId>
    </dependency>
    <dependency>
        <groupId>com.vaadin</groupId>
        <artifactId>vaadin-cdi</artifactId>
        <version>2.0.0.beta1</version>
    </dependency>
</dependencies>

Note that WebLogic includes the Oracle JDBC drivers and you don’t need to add the dependency in the pom.xml file. If you are not using WebLogic, you have to download the Oracle JDBC driver from http://www.oracle.com/technetwork/database/features/jdbc/index-091264.html and install it to your Java EE server or include it in your war package. The easiest way to do this is to install the downloaded JAR file into your local Maven repository and then include it as a standard dependency in the pom.xml file.

Configure the database connection

This guide uses JPA as a persistence technology, thus, a persistence unit must be defined. Define the following persistence unit in a new persistence.xml file in the resources/META-INF/ directory:

<?xml version="1.0"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
    <persistence-unit name="demodb">
        <jta-data-source>demoDS</jta-data-source>
        <class>com.example.Employee</class>
    </persistence-unit>
</persistence>

The previous persistence unit points to the DataSource previously defined in the application server by using the jta-data-source element.

Create the following CdiConfig class:

package com.example;

import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

public class CdiConfig {

    @Produces
    @PersistenceContext(unitName = "demodb")
    @Dependent
    public EntityManager entityManager;
}

In JPA, the interaction with this persistence unit is done through an EntityManager object. In Java EE servers you typically inject an EntityManager with the @PersistenceContext annotation. However, this guide uses Apache DeltaSpike Data, a plain CDI library, to define queries. To make the EntityManager and other CDI objects available to it through the @Inject annotation, the EntityManager must be exposed as a CDI bean as configured in the previous class.

Create an Employee class

Create the following Employee class to encapsulate the data from the employee table:

package com.example;

import javax.persistence.*;

@Entity
public class Employee {

    @Id
    @SequenceGenerator(name = "EmployeeSeq", sequenceName = "employee_seq")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "EmployeeSeq")
    private Long id;

    @Column
    private String name;

    @Column
    private String email;

    ... setters and getters ...
}

Create a repository interface

Create the following EmployeeRepository interface:

package com.example;

import org.apache.deltaspike.data.api.EntityRepository;
import org.apache.deltaspike.data.api.Repository;

import javax.transaction.Transactional;
import java.util.List;

@Repository
@Transactional
public interface EmployeeRepository extends EntityRepository<Employee, Long> {

    List<Employee> findByNameLikeIgnoreCase(String name);

} 

This interface is annotated with @Repository to allow declaring query methods. It also extends Apache DeltaSpike’s EntityRepository which defines several ready-to-use methods such as findAll, save, remove, and others. Apache DeltaSpike Data Module derives the actual queries from the methods’ names. In the previous example the "findBy..." part indicates that a list of entities is returned, the "...byName..." part refers to the name of the Java field used to filter the result, and the "...LikeIgnoreCase" part makes the filter to use the like operator. For more information about Apache DeltaSpike Data Module, see the documentation at https://deltaspike.apache.org/documentation/data.html.

Note how the repository interface is annotated with @Transactional. This makes all the methods in the interface to be transactional. In real-life applications a service layer is typically added between the UI and the data access layers. Usually, the classes (or methods) in this service layer are Stateless EJBs or CDI beans annotated with @Transactional and can use several repositories to modify different entity instances in the same transactional scope.

Because a JTA datasource was previously defined in the persistence.xml file, it’s required to configure a transaction strategy. Create an apache-deltaspike.properties file in the resources/META-INF/ directory with the following contents:

globalAlternatives.org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy=org.apache.deltaspike.jpa.impl.transaction.ContainerManagedTransactionStrategy

There are several strategies available. This tutorial uses ContainerManagedTransactionStrategy which means Apache DeltaSpike won’t control transactions at all. Instead this guide uses CDI the @Transactional annotation and the CDI container will automatically intercept the methods. You can also use Stateless EJBs which are transactional by default.

Also, create an empty beans.xml file in the webapp/WEB-INF/ directory as it is required by CDI.

Implement the UI

Create a Vaadin UI by implementing the following VaadinUI class:

package com.example;

import com.vaadin.cdi.CDIUI;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.Grid;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

import javax.inject.Inject;
import java.util.List;

@CDIUI("")
public class VaadinUI extends UI {

    @Inject
    private EmployeeRepository repository;

    private Grid<Employee> grid = new Grid<>();

    @Override
    protected void init(VaadinRequest vaadinRequest) {
        TextField filter = new TextField("Filter by name:");
        filter.addValueChangeListener(e -> updateGrid(e.getValue()));

        grid.addColumn(Employee::getName).setCaption("Name");
        grid.addColumn(Employee::getEmail).setCaption("Email");

        VerticalLayout layout = new VerticalLayout(filter, grid);
        setContent(layout);
        updateGrid("");
    }

    private void updateGrid(String filter) {
        List<Employee> employees = repository.findByNameLikeIgnoreCase("%" + filter + "%");
        grid.setItems(employees);
    }
}

This class creates a web UI containing a Grid component to show all the employees in the database and a TextField to filter the list of employees by their name. Notice how the VaadinUI class is annotated with @CDIUI. This allows Vaadin to use instances of this class when the application is opened in the web browser. Also notice how the @Inject annotation is used to inject an instance of the repository class previously created.

Running the application

Execute the following command to build and run the application (WebLogic must be running before deploying the application):

mvn clean package weblogic:deploy -Dname=demo -Dsource=target/demo.war

By default, the application should be reachable at http://localhost:7001/demo. The following is a screenshot of the running application:

Screen Shot

See the complete source code on GitHub

Learn ES6 Today with Vaadin Elements - Part 1: Promises

The Open Web Platform is moving faster than ever, with an ever evolving set of tools, features, and technologies. It is sometimes difficult to keep up with updated APIs, new constructs, and changes to existing workflows.

In this blog series, I’ll show you some new features of the web platform that you can start working with today. Over the course of several posts, you’ll learn better and easier ways to accomplish the same task.

We will be using an example using <vaadin-grid> to show the difference between the old method and the new features of ES6. In this example, we will cover Promises as a way to write asynchronous code in a top-down way.

I've divided this guide into the "old" way of using callbacks for asynchronous functions, and the ES6 way of using Promises. Skip ahead to learn just about Promises.

 

What is ES6?

First let’s quickly go over some basic terms and concepts. The latest version of Javascript is called ECMAScript2015, or ES2015, and more commonly known as ES6. ES6 brings a host of new updates to the language, some of which we’ll cover in this blog series.

ECMAScript (ES) is the standardized language that Javascript implements. The two are nearly synonymous today and the exact distinction is unimportant.
See this post by Ben McCormick for a brief explanation.

The “old” way

In this short example, we will populate a <vaadin-grid> with a set of random users with their nationalities written out in full.

You can view and play with the sample on jsbin here.

Note: I will skip the basics of imports, styles and <vaadin-grid> HTML setup. See our Vaadin Elements guide for an introduction to <vaadin-grid>.

[.. imports, styles, and HTML elided ..]

<script>
  var grid = document.getElementById('grid');

  // grid.items is called implicitly to fill in the grid
  grid.items = function(params, callback) {


    // First grab a list of users
    var url = 'https://randomuser.me/api?index=' + params.index + '&results=' + params.count;
    getJSON(url, function(users) {
      
      // Then replace the short nationality code with the full country name
      users.results.forEach(function(user) {
        var url2 = 'https://restcountries.eu/rest/v1/alpha/' + user.nat;
        getJSON(url2, function(natData) {
          user.nat = natData.name;
          accumulate(user);
        })
      });
      
      // Gather final asynchronous results
      var waiting = users.results.length;
      var results = [];
      var accumulate = function(entry) {
        results.push(entry);
        waiting--;
        
        if (waiting == 0) {          
          // Call grid's callback to populate grid
          callback(results);
        }
      };
    });
  };

  // Send a request to url and call callback with the results
  function getJSON(url, callback) {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
      if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
        callback(JSON.parse(xhr.responseText));
      }
    };
    xhr.open('GET', url, true);
    xhr.send();
  }
</script>

Getting the data

This guide is all about populating the grid with items, so our work will be in the grid.items function. grid.items is a function that is called implicitly by the grid element when it loads data from the server.

First we will request a list of users from http://randomusers.me with first name, last name, and a three letter nationality abbreviation.
We will then send requests to https://restcountries.eu/ to get the full nationality text for each user.
Lastly, we will compile the full list of users with long form nationalities and call the grid items callback with that list, which will populate our grid.

Here it is step by step:

First make a request for the users and pass in a callback function to the request.

// First grab a list of users
var url = 'https://randomuser.me/api?index=' + params.index + '&results=' + params.count;
getJSON(url, function(users) { 
  ... 
});

Then for each user in the retrieved data, we will run another request to get the full nationality name.

getJSON(url, function(users) {   
  users.results.forEach(function(user) {
    var url2 = 'https://restcountries.eu/rest/v1/alpha/' + user.nat;
    getJSON(url2, function(natData) {
      user.nat = natData.name;
      accumulate(user);
    });
   });

This calls the accumulate(entry) function, which gathers a new copy of the user, now with the full nationality.

Because our HTTP request are asynchronous, we have to wait until all of them are complete before we can finally run the grid items callback and populate the grid.

// Gather final asychronous results
var waiting = data.results.length;
var finalList = [];
var accumulate = function(user) {
  finalList.push(entry);
  waiting--;

  if (waiting == 0) {          
    // Call grid's callback to populate grid
    callback(finalList);
  }
};

If you are confused by this example, don’t feel bad, I was, too. The problem with this kind of asynchronous code is the necessity of callbacks to signal when an async request has completed. This is sometimes known as “callback hell” and is what happens when you try to write top-down code where one function requires waiting for another function to complete. It’s messy, but there are ways to make more readable code.

The promise of Promises

What if you could write similar straightforward code without having to pass around a myriad callbacks? ES6 finally brings Promises to Javascript, which make writing asynchronous code much simpler. Promises have been around for awhile in other languages and libraries, but ES6 brings first class Promises directly to the language.

A Promise represent a value that will be available some time in the future. This means the Promises are a stand-in for the result of some asynchronous behaviour. You work with Promises as though data is already available and write code top down that consumes that data.

Quick introduction to Promises

You create a Promise by calling its constructor and passing in a function with two arguments, which themselves are functions, so:

var promise = new Promise(function(resolve, reject) { ... });

This sets up a Promise which resolves to a value when resolve is called. For example:

var x = new Promise(function(resolve, reject) {
  // do some work
  resolve(10);
});

Which will set the value of x to a Promise that resolves to 10 once it has finished its work. But we can’t actually use that value of 10 until the Promise is consumed. To consume this value, we need to use Promise.then().

Promise.then() takes a function for input with an argument representing the fulfilled value of the Promise. Inside the then function, we can actually use the resolved value of the Promise.

For example:

var y = new Promise(function(resolve, reject) {
  // do some work
  resolve(20);
}).then(function(value) {
  var result = value + 5;
  console.log(result);
});

This will create a Promise which will eventually resolve to 20 that will be consumed by the then function and print 25 to the console.

You can also chain then statements to do extended work with Promises. If you return a value inside a then function, then you will return a new Promise that will resolve to the returned value.

var z = new Promise(function(resolve, reject) {
  // do some work
  resolve(5);
}).then(function(value) {
  var result = value + 20;
  return result;
}).then(function(value) {
  var finalresults = value + 30;
  console.log(finalresults);
});

This will create a Promise that resolves to 5, which then is chained with that value to another Promise, which will eventually resolve to 25. Then we use that new value to log 55 to the console.

Now let’s return to our “callback hell” example and make it work in a more straightforward manner.

Note that I gloss over the reject aspect of Promises. In addition to resolving a Promise, you can reject if the Promise throws an error or for whatever reason cannot resolve to a value. We also ignored error checking in our first example for simplicity’s sake.

You can read more about Promises and error checking at MDN here.

The ES6 way

We’re going to rewrite the logic in the example using Promises.
You can follow along and try out the new code at jsbin here.

[ ... HTML and imports elided ... ]


<script>
  var grid = document.getElementById('grid');

  grid.items = function(params, callback) {
    var url = 'https://randomuser.me/api?index=' + params.index + '&results=' + params.count;
    
    getJSONPromise(url).then(function(users) {
      var promises = users.results.map(function(user) {
        var url2 = 'https://restcountries.eu/rest/v1/alpha/' + user.nat;
        
        return getJSONPromise(url2).then(function(natData) {
          user.nat = natData.name;          
          return entry;
        });
      });
      
      return Promise.all(promises);
    }).then(function(results) {
      callback(results);
    });
  };

  function getJSONPromise(url) {
    return new Promise(function(resolve, reject) {
      var xhr = new XMLHttpRequest();
      xhr.onreadystatechange = function() {
        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
          resolve(JSON.parse(xhr.responseText));
        }
      };
      xhr.open('GET', url, true);
      xhr.send();
    });
  }
</script>

Setup

First, we rewrite the getJSON function to return a Promise that will resolve when the HTTP request return and call it getJSONPromise.

function getJSONPromise(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
      if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
        resolve(JSON.parse(xhr.responseText));
      }
    };
    xhr.open('GET', url, true);
    xhr.send();
  });
}

This is exactly the same as before, but here we return a new Promise that resolves to the results when the request is complete. We can now rewrite our callback chain into a chain of Promises.

Promises, Promises

The first part of the code looks exactly the same, except now we call getJSONPromise and instead of passing in a callback to be called when the request is done, we instead just chain a then to the Promise.

var grid = document.getElementById('grid');

grid.items = function(params, callback) {
  var url = 'https://randomuser.me/api?index=' + params.index + '&results=' + params.count;
    
  getJSONPromise(url).then(...);
};

Next let’s look at the body of the then function, which requires a little bit of understanding.

getJSONPromise(url).then(function(users) {
  var promises = users.results.map(function(user) { ... return a Promise ... });
  ... 
}); 

What we are doing here is first creating an array of Promises by mapping a function onto the array of users. See this MDN entry to learn more about Array.map()

Let’s take a look at the function being mapped onto the list of users.

var promises = users.results.map(function(user) {
  var url2 = 'https://restcountries.eu/rest/v1/alpha/' + user.nat;
      
  return getJSONPromise(url2).then(function(natData) {
    user.nat = natData.name;          
    return entry;
  });
});

Here we send a new HTTP request for each user and when that request completes, we set its 3 letter nationality to the full nationality. We then return entry which implicitly creates a new Promise that resolves to the value of the update user. The new Promise for each user is then added to an array of Promises called promises.

And then finally we bring it all together.

getJSONPromise(url).then(function(users) {
  var promises = ...

  return Promise.all(promises);
}).then(function(results) {
  callback(results);
});

We then return Promise.all(promises) which returns a new Promise that resolves when all of the Promises in the array promises themselves resolve. The returned Promise will resolve to an array of values: one for each entry in promises. The resolved array in turn is an array of our users with their nationality filled in.

From there, we just chain a function that will run the grid.items callback function callback, which will finally populate the grid just like in the first code sample.

Conclusion

Now you’ve seen the power of Promises to make writing asynchronous code a little easier. Promises are available in all modern browsers, with the exception of Internet Explorer and Opera Mini.

This is the first in a series of posts about ES6, so stay tuned for more. Next time, I’ll cover the new Fetch API, which makes writing HTTP Requests a breeze.

Learn more about <vaadin-grid> and Vaadin Elements