Consuming a RESTful service with Ajax

In this tutorial you will consume a RESTful service (that we deploy on WildFly application server) with a simple Ajax Client.

Let’s start from the REST Endpoint:

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import java.util.List;
 
 
@Path("persons")
@ApplicationScoped
@Produces("application/json")
@Consumes("application/json")
public class DemoEndpoint {
 
    @Inject DemoRepository demoRepository;
 
    @GET
    public List<Person> getAll() {
        return demoRepository.findAll();
    }
 
    @POST
    public Response create(Person p) {
        demoRepository.createPerson(p);
        return Response.status(201).build();
 
    }
 
    @PUT
    public Response update(Person p) {
        demoRepository.updatePerson(p);
        return Response.status(204).build();
    }
    @DELETE
    public Response delete(@QueryParam("id") Integer id) {
        demoRepository.deletePerson(id);
        return Response.status(204).build();
    }
 
}

As you can see, this REST Service contains CRUD methods to perform the basic operations on the Person class.

The Repository class uses a plain memory structure to store your objects:

package com.packt.quarkus.chapter4;

import javax.enterprise.context.ApplicationScoped;
import java.util.ArrayList;
import java.util.List;


@ApplicationScoped
public class DemoRepository {

    List<Person> list = new ArrayList();
    int counter;

    public int getNextCustomerId() {
        return counter++;
    }

    public List<Person> findAll() {
        return list;
    }

    public Person findPersonById(Integer id) {
        for (Person c: list) {
            if (c.getId().equals(id))  {
                return c;
            }
        }
        throw new RuntimeException("Person not found!");
    }

    public void updatePerson(Person person) {
        Person personToUpdate = findPersonById(person.getId());
        personToUpdate.setName(person.getName());
        personToUpdate.setSurname(person.getSurname());
    }

    public void createPerson(Person person) {
        person.setId(getNextCustomerId());
        findAll().add(person);
    }

    public void deletePerson(Integer id) {
        Person c = findPersonById(id);
        findAll().remove(c);
    }
}

Then, here is the POJO Class named Person:

public class Person {
    private Integer id;
    private String name;
    private String surname;

    @Override
    public String toString() {
        return "Person{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", surname='" + surname + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }
}

Finally, include a REST Activator which activates REST Services under the URI “/rest”:

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/rest")
public class JaxRsActivator extends Application {
   /* class body intentionally left blank */
}

 Done with the server side. Now let’s add an index.hml page to display the List of Person in an HTML Table:

<html>
<head>
    <!-- little bit of css to beutify the table -->
    <link rel="stylesheet" type="text/css" href="stylesheet.css" media="screen" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>


    <script type="text/javascript">

$(document).ready(function () {
    $.getJSON("http://localhost:8080/rest/app/persons",
    function (json) {
        var tr;
        for (var i = 0; i < json.length; i++) {
            tr = $('<tr/>');
            tr.append("<td>" + json[i].id + "</td>");
            tr.append("<td>" + json[i].name + "</td>");
            tr.append("<td>" + json[i].surname + "</td>");
            $('table').append(tr);
        }
    });
});
</script>
</head>

<body>
<div align="left" style="margin-top: 10%;">
    <fieldset style="border: none;">
        <legend><strong>Users List</strong></legend>

        <!-- table to show data -->
        <table class="greyGridTable">
            <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Surname</th>
            </tr>
            </thead>
            <tbody>

            </tbody>
        </table>
    </fieldset>
</div>

</body>
</html>

Some CSS style has been included in the file stylesheet.css:

table.greyGridTable {
  border: 2px solid #FFFFFF;
  width: 100%;
  text-align: center;
  border-collapse: collapse;
}
table.greyGridTable td, table.greyGridTable th {
  border: 1px solid #FFFFFF;
  padding: 3px 4px;
}
table.greyGridTable tbody td {
  font-size: 13px;
}
table.greyGridTable td:nth-child(even) {
  background: #EBEBEB;
}
table.greyGridTable thead {
  background: #FFFFFF;
  border-bottom: 4px solid #333333;
}
table.greyGridTable thead th {
  font-size: 15px;
  font-weight: bold;
  color: #333333;
  text-align: center;
  border-left: 2px solid #333333;
}
table.greyGridTable thead th:first-child {
  border-left: none;
}

table.greyGridTable tfoot td {
  font-size: 14px;
}

That’s all. Make sure the following dependencies are included in your pom.xml file:

<dependency>
      <groupId>org.jboss.spec.javax.ws.rs</groupId>
      <artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
      <scope>provided</scope>
</dependency>
<dependency>
      <groupId>javax.enterprise</groupId>
      <artifactId>cdi-api</artifactId>
      <scope>provided</scope>
</dependency>

Deploy the application with

$ mvn package wildfly:deploy

Now we can try adding some data with cURL:

$ curl -d '{"name":"john", "surname":"black"}' -H "Content-Type: application/json" -X POST http://127.0.0.1:8080/persons
$ curl -d '{"name":"victor", "surname":"frankenstein"}' -H "Content-Type: application/json" -X POST http://127.0.0.1:8080/persons

Let’s surf to localhost:8080 to check that Data is listed in our table:

Great. We could see how easily JSON data can be parsed with some help from jQuery and Ajax!

RESTAssured tutorial

This tutorial discusses about RESTAssured, a Testing framework that is used by several frameworks (including Quarkus) to test specifically REST applications.

REST Assured is a Java API that can be used to validate RESTful services through its fluent DSL (Domain specific Languages) that describes a connection to an HTTP endpoint and expected results.

Here is a REST Assured Hello World example:

import static com.jayway.restassured.RestAssured.given;

import org.junit.Test;

public class MinimalRestAssured {

 @Test
 public void checkSiteIsUp() {
	 given().when().get("http://www.acme.com").then().statusCode(200);
 }

}

This minimal example shows how to connect to an HTTP resource through a GET call and verify that the HTTP code 200/success is returned. Notice that we don’t need to use typical assert expressions (like JUnit classes do) as this si done behind the hoods by RESTAssured if there is a mismatch between your expression and the result. Now let’s see a more complex example where we check the body returned as JSON. In order to do that, we first need to import some more classes:

import io.restassured.RestAssured.*
import io.restassured.matcher.RestAssuredMatchers.*
import org.hamcrest.Matchers.*

Now let’s see practical example.

Suppose you have the following JSON returned by the URI “/cars” :

{
  "year": "2001",
  "make": "Ford",
  "model": "Coupe"
}

You can verify that URI returns a status code of 200 and the body content using this fluent API:

@Test
public void testCustomerService() {

        given()
        .when().get("/cars")
        .then()
        .statusCode(200)
        .body(containsString("Ford"),
              containsString("Coupe"));

}

You can also verify exact values:

@Test
public void testCustomerService() {

        given()
        .when().get("/cars")
        .then()
        .statusCode(200)
        .body("year", is(1987));
}

RESTAssured using Parameters

What if your REST Test uses parameters? Here is how you can Test against Path Params:

@Test
public void testListOfUsers() {
    given()
        .contentType(ContentType.JSON)
        .pathParam("id", "12345")
    .when()
        .get("/users/{id}")
    .then()
        .statusCode(200)
        .body("firstName", equalTo("John"))
        .body("Surname", equalTo("Doe"));
}

And here is how you can test agains Query Parameters using as bonus also an Header Verification:

@Test
public void testListOfUsers() {
       given()
            .header("Authorization", authorizationHeader)
            .accept(JSON)
            .queryParam("id", "1")
            .when()
            .get("/users")
            .then()
            .statusCode(200)
            .body("firstName", equalTo("John"))
            .body("Surname", equalTo("Doe"));
   
}

How to validate a response which contains an array of data

Applications which are producing tabular data from a Database, typically a response with an Array of JSON objects. To validate the content of the response, you can reference the single array data as in the following example:

@Test
public void testListOfUsers() {
   RestAssured.given()
        .when().get()
        .then()
        .statusCode(200)
        .body("$.size()", is(3),
                "[0].id", is(1),
                "[0].name", is("Batman"),
                "[1].id", is(2),
                "[1].name", is("Superman"),
                "[2].id", is(3),
                "[2].name", is("Wonder woman")
        );
   
}

Extracting Manually the Response

In some cases, it could be required to fetch the JSON Response so that we perform some actions with it. Here is how to fetch in a JSONArray the Response returned from an HTTP GET Test:

public void testGetListOfUsers() {
    Response response = given()
            .accept(JSON)
            .when()
            .get("/users")
            .then()
            .statusCode(200)
            .contentType(JSON)
            .extract()
            .response();
    String jsonBody = response.getBody().asString();
    try {
        JSONArray usersArray = new JSONArray(jsonBody);
        assertNotNull(usersArray);
        assertTrue(usersArray.length() > 0);
    } catch (JSONException ex) {
        fail(ex.getLocalizedMessage());
    }
}

XML Testing with REST assured

XML can be verified in a similar way. Imagine that a POST request to http://localhost:8080/users returns:

<greeting>
   <firstName>John</firstName>
   <lastName>Doe</lastName>
</greeting>

You can easily perform and verify the firstName with REST assured:

given().
         parameters("firstName", "John", "lastName", "Doe").
when().
         post("/greetXML").
then().
         body("greeting.firstName", equalTo("John")).

If you want to verify both firstName and lastName you may do like this:

given().
         parameters("firstName", "John", "lastName", "Doe").
when().
         post("/greetXML").
then().
         body("greeting.firstName", equalTo("John")).
         body("greeting.lastName", equalTo("Doe"));

Testing other HTTP methods

RESTAssured can be tested to verify all HTTP methods (GET/POST/PUT/DELETE). You can attach the HTTP method in the fluent expression as in the following example:

    @Test
    public void testCar() {

        JsonObject car = Json.createObjectBuilder()
                .add("make", "Fiat")
                .add("model", "500").build();

        // Testing HTTP POST
        given()
                .contentType("application/json")
                .body(car.toString())
                .when()
                .post("/cars")
                .then()
                .statusCode(201);

        // Testing HTTP GET
        given()
                .when().get("/cars")
                .then()
                .statusCode(200)
                .body(containsString("Fiat"),
                      containsString("500"));

        car = Json.createObjectBuilder()
                .add("id", "1")
                .add("make", "Opel")
                .add("model", "Karl").build();

        // Testing HTTP PUT
        given()
                .contentType("application/json")
                .body(obj.toString())
                .when()
                .put("/cars")
                .then()
                .statusCode(204);

        // Testing HTTP DELETE
        given()
                .contentType("application/json")
                .when()
                .delete("/users?id=1")
                .then()
                .statusCode(204);

    }

Compiling REST Assured applications

If you are using Maven to build your project, you need to add the following dependency to your pom.xml:

<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>rest-assured</artifactId>
    <version>4.0.0</version>
    <scope>test</scope>
</dependency>

Also, as REST-assured leverages the Hamcrest matchers to perform its assertions, we must include that dependency as well:

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-all</artifactId>
    <version>1.3</version>
</dependency>

That’s all! Enjoy coding with RESTAssured

Securing JAX-RS Services in WildFly applications

In this tutorial we will learn how to secure a JAX-RS Service using an Elytron File System Realm, restricting access to methods using javax.annotation.security annotations.

This example will be deployed on WildFly application server so, in order to get started, download the latest WildFly release from http://www.wildfly.org

In order to get started we will at first configure the File System Realm on Elytron and then we will code our JAX-RS secured application.

Defining the File System Realm

A File System Realm in Elytron is a simple security realm definition backed by the filesystem. When using the management CLI, you will need defining at first the Identities to be added to the Realm, then add a simple-role-decoder, configure a security-domain, and finally configure an http-authentication-factory that needs to be set on undertow. The following script will do it:

connect

batch

/subsystem=elytron/filesystem-realm=demoFsRealm:add(path=demofs-realm-users,relative-to=jboss.server.config.dir)
 
 
/subsystem=elytron/filesystem-realm=demoFsRealm:add-identity(identity=frank)
/subsystem=elytron/filesystem-realm=demoFsRealm:set-password(identity=frank,clear={password="password123"})
/subsystem=elytron/filesystem-realm=demoFsRealm:add-identity-attribute(identity=frank,name=Roles, value=["Admin","Guest"])
 
/subsystem=elytron/simple-role-decoder=from-roles-attribute:add(attribute=Roles)

 
/subsystem=elytron/security-domain=fsSD:add(realms=[{realm=demoFsRealm,role-decoder=from-roles-attribute}],default-realm=demoFsRealm,permission-mapper=default-permission-mapper)

 
/subsystem=elytron/http-authentication-factory=example-fs-http-auth:add(http-server-mechanism-factory=global,security-domain=fsSD,mechanism-configurations=[{mechanism-name=BASIC,mechanism-realm-configurations=[{realm-name=RealmUsersRoles}]}])

 
/subsystem=undertow/application-security-domain=httpFsSD:add(http-authentication-factory=example-fs-http-auth)

run-batch

reload

Execute the above CLI script and move on to the next section.

Coding the Secured JAX-RS application

Now let’s start coding a simple JAX-RS Endpoint:

package com.mastertheboss;

import org.json.JSONObject;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.auth.server.SecurityIdentity;
import org.wildfly.security.authz.Attributes;

import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;

@PermitAll
@Path("/")
public class Endpoint {

    @Context
    private SecurityContext securityContext;

    @GET
    @Path("/info")
    @Produces(MediaType.APPLICATION_JSON)
    @RolesAllowed({"Admin"})
    public String getInfo() {
        return identityInfoAsJson();
    }

    @POST
    @RolesAllowed({"Admin"})
    @Consumes(MediaType.APPLICATION_JSON)
    public Response saveData(Data data) {

        System.out.println(data);
        return Response.ok("Done!").build();
    }

    private String identityInfoAsJson() {
        SecurityIdentity identity = SecurityDomain.getCurrent().getCurrentSecurityIdentity();
        JSONObject response = new JSONObject();
        response.put("name", securityContext.getUserPrincipal().getName());
        response.put("attributes", attributesAsJson(identity.getAttributes()));
        return response.toString();
    }

    private JSONObject attributesAsJson(Attributes attributes) {
        JSONObject attributesObject = new JSONObject();
        for (String attribute : attributes.keySet()) {
            attributesObject.put(attribute, attributes.get(attribute));
        }
        return attributesObject;
    }

}

In this example, we have two methods that are restricted to users belonging to the “Admin” Role. The method getInfo will simply print the user’s identity information. The method saveData, on the other hand, consumes a JSON and requires as well a valid user with the “Admin” Role. Also the saveData method consumes as input a Java Bean named “Data” which contains a single property:

package com.mastertheboss;

public class Data {

    String key;

    @Override
    public String toString() {
        return "Data{" +
                "key='" + key + '\'' +
                '}';
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}

We need a JAX-RS activator in order to activate the REST Service:

package com.mastertheboss;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

 
@ApplicationPath("rest")
public class JAXActivator extends Application {
    // Left empty intentionally
}

Within the web.xml file, we need to provide the following information:

<web-app>
    <security-constraint>
        <web-resource-collection>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>Admin</role-name>
        </auth-constraint>
    </security-constraint>

    <security-role>
        <role-name>Admin</role-name>
    </security-role>

    <context-param>
        <param-name>resteasy.role.based.security</param-name>
        <param-value>true</param-value>
    </context-param>

    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>File System basic Realm</realm-name>
    </login-config>
</web-app>

As you can see, besides the Security-Role, we have set to true the Param resteasy.role.based.security

To get it working, we need to include in the jboss-web.xml file the reference to Elytron’s security domain:

<jboss-web>
    <security-domain>httpFsSD</security-domain>
</jboss-web>

That’s all. We can start testing our application. We will try accessing the getInfo() method from the browser:

As you can see, a basic Authentication challenge will be sent. Enter the username/password available in the Elytron Realm, and you will be allowed to the getInfo() method:

Testing the POST method

There are several ways to test the POST method. We will show in this tutorial how to do it with the curl utility. First of all, we need to generate the Authorization String, which is a base64 encoded String of the username:password. You can use the following command to do that:

AUTH=$(echo -ne "frank:password123" | base64 --wrap 0)

And now, we can call the POST method, passing as data the JSON and setting the Authorization Header with the encoded username:password:

curl \
  --header "Content-Type: application/json" \
  --header "Authorization: Basic $AUTH" \
  --request POST \
  --data  '{"key":"value"}' \
 http://localhost:8080/self-service/rest/

If you followed the steps so far, you will be allowed in the saveData method of your JAX-RS Service

Please notice that since WildFly 20 you can configure RESTEasy attributes (such as resteasy.role.based.security) using MicroProfile Confiig API. Check this tutorial for more details: Configuring RESTEasy Applications

Conclusion

In this tutorial we have learned several things: first of all we have defined a File System Realm on Elytron and created one Entity in it. Then we have coded a basic JAX-RS application which requires the “Admin” user to grant access to its resources. Also, we have learnt a simple hack to invoke HTTP Resources passing the encoded Authorization in the URL.

Using REST Services to upload and download files

This rest service tutorial is a quick guide for handling files upload and download using REST Services. We will demonstrate with a rest service sample how to upload and download files using JAX-RS API with different front ends (AngularJS and JSP). On the top of that, we will show how to build a JUnit Test to test files uploading using RESTEasy Client API.

Uploading and Downloading files using a REST Service

  • Requirements: Know about JAX-RS. To get started we recommend this tutorial: RESTEasy tutorial

In order to manage files Upload and Download we will be using the core JAX-RS API and Rest Easy implementation, along with IOUtil class from Apache Commons IO library. Here is the source code of our Rest endpoint:

package com.mastertheboss.rest;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.core.Response.ResponseBuilder;

import org.apache.commons.io.IOUtils;
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;

@Path("/file")
public class RestFilesDemo {

	@Context
	private ServletContext context;

	@POST
	@Path("/upload")
	@Consumes(MediaType.MULTIPART_FORM_DATA)
	public Response uploadFile(MultipartFormDataInput input) throws IOException {

		Map<String, List<InputPart>> uploadForm = input.getFormDataMap();

		// Get file data to save
		List<InputPart> inputParts = uploadForm.get("attachment");

		for (InputPart inputPart : inputParts) {
			try {

				MultivaluedMap<String, String> header = inputPart.getHeaders();
				String fileName = getFileName(header);

				// convert the uploaded file to inputstream
				InputStream inputStream = inputPart.getBody(InputStream.class, null);

				byte[] bytes = IOUtils.toByteArray(inputStream);

				//String path = System.getProperty("user.home") + File.separator + "uploads";
				File customDir = new File(Config.UPLOAD_FOLDER);

				if (!customDir.exists()) {
					customDir.mkdir();
				}
				fileName = customDir.getCanonicalPath() + File.separator + fileName;
				writeFile(bytes, fileName);

				return Response.status(200).entity("Uploaded file name : " + fileName+" . <br/> <a href='"+context.getContextPath()+"'>Back</a>").build();

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	@POST
	@Path("/download")
	@Produces(MediaType.APPLICATION_OCTET_STREAM)
	@Consumes("application/x-www-form-urlencoded")
	public Response downloadFileWithPost(@FormParam("file") String file) {

		String path = System.getProperty("user.home") + File.separator + "uploads";
		File fileDownload = new File(path + File.separator + file);
		ResponseBuilder response = Response.ok((Object) fileDownload);
		response.header("Content-Disposition", "attachment;filename=" + file);
		return response.build();
	}

	@GET
	@Path("/download")
	@Produces(MediaType.APPLICATION_OCTET_STREAM)
	public Response downloadFileWithGet(@QueryParam("file") String file) {
		System.out.println("Download file "+file);
		File fileDownload = new File(Config.UPLOAD_FOLDER + File.separator + file);
		ResponseBuilder response = Response.ok((Object) fileDownload);
		response.header("Content-Disposition", "attachment;filename=" + file);
		return response.build();
	}

	@GET
	@Path("/list")
	@Produces(MediaType.APPLICATION_JSON)
	public List<String> listFiles() {

		List<String> listFiles = new ArrayList<>();
		File fileFolder = new File(Config.UPLOAD_FOLDER);
		File[] list = fileFolder.listFiles();

		for (File f: list) {
			if (!f.isDirectory()) {
				listFiles.add(f.getName());
			}
		}
		return listFiles;
	}

	private String getFileName(MultivaluedMap<String, String> header) {

		String[] contentDisposition = header.getFirst("Content-Disposition").split(";");

		for (String filename : contentDisposition) {
			if ((filename.trim().startsWith("filename"))) {
				String[] name = filename.split("=");
				String finalFileName = name[1].trim().replaceAll("\"", "");
				return finalFileName;
			}
		}
		return "unknown";
	}
	// Utility method
	private void writeFile(byte[] content, String filename) throws IOException {
		File file = new File(filename);

		if (!file.exists()) {
			file.createNewFile();
		}
		FileOutputStream fop = new FileOutputStream(file);
		fop.write(content);
		fop.flush();
		fop.close();
	}
}

Following here are some details about the methods used:

  • listFiles: this method returns the list of Files which are available in the Config.UPLOAD_FOLDER
  • uploadFile(MultipartFormDataInput input) : this method uploads a File, consuming a MULTIPART_FORM_DATA from an HTTP POST method
  • downloadFileWithPost(@FormParam(“file”) String file) : this method downloads a File, producing a Binary Stream, from an HTTP POST passing the filename as a Form argument
  • downloadFileWithGet(@QueryParam(“file”) String file): this method also downloads a File but it includes the filename as Query argument

Next, to ensure that the “uploads” folder exists, the following Context Listener will create the folder after deploying the application:

package com.mastertheboss.rest;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.io.File;

@WebListener
public class WebContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        if (!new File(Config.UPLOAD_FOLDER).exists()) {
            new File(Config.UPLOAD_FOLDER).mkdir();
        }

    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        //. . .
    }
}

Coding the Front end for the Rest Service sample

Even if you could use any REST Client (even cURL) to reach the REST Service, we will provide a simple front-end application to interact with the Rest endpoint.

Firstly, create the following index.jsp page which uses an AngularJS Controller to interact with our REST Service:

<html>
<head>
 <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" media="screen" />

<script type = "text/javascript">
	angular.module('app', [])
	.controller('ctrl', function ($scope, $http) {
		$scope.files = [];

		$scope.loadData = function () {
			$http({
				method: 'GET',
				url: '/rest-file-manager/rest/file/list'
			}).then(function successCallback(response) {
				$scope.files = response.data;
			}, function errorCallback(response) {
				console.log(response.statusText);
			});
		}

		$scope.downloadFile = function (filename) {

			$http({
				method: 'GET',
				url: '/rest-file-manager/rest/file/download',
				params: {
					file: filename
				},
				responseType: 'arraybuffer'

			}).success(function (data, status, headers) {

				headers = headers();

				var contentType = headers['content-type'];
				var linkElement = document.createElement('a');

				try {

					var blob = new Blob([data], {
						type: contentType
					});

					var url = window.URL.createObjectURL(blob);

					linkElement.setAttribute('href', url);
					linkElement.setAttribute("download", filename);

					var clickEvent = new MouseEvent("click", {

						"view": window,
						"bubbles": true,
						"cancelable": false

					});

					linkElement.dispatchEvent(clickEvent);

				} catch (ex) {
					console.log(ex);
				}

			}).error(function (data) {
				console.log(data);
			});

		};

		//call loadData when controller initialized
		$scope.loadData();
	})
</script>

<title>REST Download demo with AngularJS</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body ng-app='app' ng-controller='ctrl'>
    <h1>REST Upload/Download demo using AngularJS</h2>
	<h2>Upload file</h2>
	<form method="post" action="rest/file/upload"
		enctype="multipart/form-data">
		<input type="hidden" name="action" value="upload" /> <label>Load
			your file:</label> <input type="file" name="attachment" /> <br /> <input
			type="submit" value="Upload file" />
	</form>

	<h2>Download file</h2>

        <div class="divTable blueTable">
                <div class="divTableHeading">
                    <div  class="divTableHead">File Name</div>
                    <div  class="divTableHead">Action</div>
                </div>
                <div class="divTableRow" ng-repeat="file in files">
                    <div class="divTableCell">{{ file }}</div>
                    <div class="divTableCell"><a ng-click="downloadFile( file )" class="myButton">Download</a> </div>
                </div>
            </div>
</body>
</html>

Next, deploy the example application using:

mvn clean install wildfly:deploy

Finally, here is how our Rest Endpoint in action after we have uploaded an example XML file:

JSP Version

Within the source code for this rest service tutorial, you will also find the following index2.jsp page which uses a simple JSP approach to perform the same thing:

 
       <%@page import="java.io.*, com.mastertheboss.rest.Config" %>
        <%@page import="java.util.*" %> 
        <%!        public void GetDirectory(String a_Path, Vector a_files, Vector a_folders) {
                File l_Directory = new File(a_Path);
                File[] l_files = l_Directory.listFiles();

                for (int c = 0; c < l_files.length; c++) {
                    if (l_files[c].isDirectory()) {
                        a_folders.add(l_files[c].getName());
                    } else {
                        a_files.add(l_files[c].getName());
                    }
                }


            }
        %> 


<html>
<head>
<title>Start Page</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
	<h2>REST Upload demo</h2>
	<form method="post" action="rest/file/upload"
		enctype="multipart/form-data">
		<input type="hidden" name="action" value="upload" /> <label>Load
			your file:</label> <input type="file" name="attachment" /> <br /> <input
			type="submit" value="Upload file" />
	</form>

	<h2>REST Download demo</h2>
	<form method="POST" action="rest/file/download">
		File name: <input type="text" name="file"> 
		<input type="submit">


	</form>
	
	<h2>Current Directory $HOME/uploads:</h2>
	<br/>
	        <%
            Vector l_Files = new Vector(), l_Folders = new Vector();
            GetDirectory(Config.UPLOAD_FOLDER, l_Files, l_Folders);
            out.println("<ul>");   
            for (int a = 0; a < l_Files.size(); a++) {
                out.println("<li>" + l_Files.elementAt(a).toString() + "</li>");
            }
            out.println("</ul>");
           
        %> 
</body>
</html>

Here is our simple JSP front-end in action:

Adding a JUnit Test class to test REST upload

Required: Know about JUnit framework. Check this tutorial to learn more: Getting started with JUnit 5

Finally, we include in this tutorial sample a JUnit Test class which shows the hardest part of it: managing MultiPart requests to upload a File with REST Easy Client API:

package com.mastertheboss.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.*;

import javax.ws.rs.client.Entity;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
import org.junit.Test;

public class TestRest {

	String FILENAME="test-file.txt";
	@Test
    public void sendFile() throws Exception {
         
    	ResteasyClient client = new ResteasyClientBuilder().build();
    	ResteasyWebTarget target = client.target("http://localhost:8080/rest-file-manager/rest/file/upload");
    	
    	MultipartFormDataOutput mdo = new MultipartFormDataOutput();

    	createFile();

    	File filePath = new File(FILENAME);
    	
    	// Check that file exists

    	assertTrue(filePath.exists());
    	
    	mdo.addFormData("attachment", new FileInputStream(filePath),
    		    MediaType.APPLICATION_OCTET_STREAM_TYPE,filePath.getName());
    	
    	GenericEntity<MultipartFormDataOutput> entity = new GenericEntity<MultipartFormDataOutput>(mdo) {};
    	
    	Response r = target.request().post( Entity.entity(entity, MediaType.MULTIPART_FORM_DATA_TYPE));
    	
    	// Check that upload was successful
    	assertEquals(r.getStatus(), 200);
    	
    	 
}

	private void createFile() {
		try {
			PrintWriter writer = new PrintWriter(FILENAME, "UTF-8");
			writer.println("Some text");
			writer.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

}

Here is the pom.xml file to build and deploy the project:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.mastertheboss</groupId>
	<artifactId>rest-file-manager</artifactId>
	<version>1.1</version>
	<packaging>war</packaging>

	<name>rest-file-manager</name>

	<properties>
		<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<version.server.bom>19.0.0.Final</version.server.bom>

	</properties>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.wildfly.bom</groupId>
				<artifactId>wildfly-jakartaee8-with-tools</artifactId>
				<version>${version.server.bom}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>

		<dependency>
			<groupId>org.jboss.spec.javax.servlet</groupId>
			<artifactId>jboss-servlet-api_4.0_spec</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.6</version>
		</dependency>

		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.2</version>
		</dependency>

		<dependency>
			<groupId>org.jboss.resteasy</groupId>
			<artifactId>resteasy-jaxrs</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.jboss.resteasy</groupId>
			<artifactId>resteasy-multipart-provider</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.jboss.spec.javax.ws.rs</groupId>
			<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
			<scope>provided</scope>
		</dependency>


		<dependency>
			<groupId>org.jboss.resteasy</groupId>
			<artifactId>resteasy-client</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>

		<finalName>rest-file-manager</finalName>
		<plugins>
			<plugin>
				<groupId>org.wildfly.plugins</groupId>
				<artifactId>wildfly-maven-plugin</artifactId>
				<version>2.0.0.Final</version>
				<configuration>
					<filename>${project.build.finalName}.war</filename>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<compilerArguments>
						<endorseddirs>${endorsed.dir}</endorseddirs>
					</compilerArguments>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.3</version>
				<configuration>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId>
				<version>2.6</version>
				<executions>
					<execution>
						<phase>validate</phase>
						<goals>
							<goal>copy</goal>
						</goals>
						<configuration>
							<outputDirectory>${endorsed.dir}</outputDirectory>
							<silent>true</silent>
							<artifactItems>
								<artifactItem>
									<groupId>javax</groupId>
									<artifactId>javaee-endorsed-api</artifactId>
									<version>7.0</version>
									<type>jar</type>
								</artifactItem>
							</artifactItems>
						</configuration>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
			</plugin>
		</plugins>

	</build>
	<profiles>
		<profile>
			<id>install</id>
			<activation>
				<activeByDefault>true</activeByDefault>
			</activation>
			<properties>
				<maven.test.skip>true</maven.test.skip>
			</properties>
		</profile>
		<profile>
			<id>test</id>
			<properties>
				<maven.test.skip>false</maven.test.skip>
			</properties>
		</profile>
	</profiles>
</project>

To run the test, use the ‘test’ profile as follows:

$ mvn -Ptest test

Testing the Rest Service sample cURL

You can test Files uploading as well with cURL:

$ curl -F file=@"/tmp/file.txt" http://localhost:8080/rest-file-manager/rest/file/upload
Successfully uploaded - file.txt

Source code of this Rest service tutorial

You can find the source code for this rest service tutorial here: https://github.com/fmarchioni/mastertheboss/tree/master/javaee/rest-file-manager .

Spring Boot users? Check this tutorial to learn how to manage Files Upload and Download using Spring Boot.

Creating a REST Application with JBoss Forge in a minute

This is an handy tutorial to show how to scaffold a REST application based on an Entity object using JBoss Forge. ScreenCast included!

Pre-requisites: You need JBoss Forge and WildFly application server.

You can download Forge using:

wget http://downloads.jboss.org/forge/releases/3.9.0.Final/forge-distribution-3.9.0.Final-offline.zip

You can download WildFly using:

wget http://download.jboss.org/wildfly/12.0.0.Final/wildfly-12.0.0.Final.zip

Now move into the bin folder of your forge-distribution and launch forge:

$ forge

As first step we will create a new Project named “demo-rest” which uses JAVA EE 7 stack

project-new  --named demo-rest --type war --stack JAVA_EE_7

We will then add an Entity object named Customer

jpa-new-entity --named Customer

jpa-new-field --named firstname
jpa-new-field --named surname
jpa-new-field --named email

Now we move to the top of our project and we generate the REST Endpoints to CRUD our Entities:

cd ~~
rest-generate-endpoints-from-entities --targets org.demo.rest.model.Customer 

As you can see, the following EJB Rest Resource has been created:

@Stateless
@Path("/customers")
public class CustomerEndpoint {
	@PersistenceContext(unitName = "demo-rest-persistence-unit")
	private EntityManager em;

	@POST
	@Consumes("application/json")
	public Response create(Customer entity) {
		em.persist(entity);
		return Response.created(
				UriBuilder.fromResource(CustomerEndpoint.class)
						.path(String.valueOf(entity.getId())).build()).build();
	}

	@DELETE
	@Path("/{id:[0-9][0-9]*}")
	public Response deleteById(@PathParam("id") Long id) {
		Customer entity = em.find(Customer.class, id);
		if (entity == null) {
			return Response.status(Status.NOT_FOUND).build();
		}
		em.remove(entity);
		return Response.noContent().build();
	}

	@GET
	@Path("/{id:[0-9][0-9]*}")
	@Produces("application/json")
	public Response findById(@PathParam("id") Long id) {
		TypedQuery<Customer> findByIdQuery = em
				.createQuery(
						"SELECT DISTINCT c FROM Customer c WHERE c.id = :entityId ORDER BY c.id",
						Customer.class);
		findByIdQuery.setParameter("entityId", id);
		Customer entity;
		try {
			entity = findByIdQuery.getSingleResult();
		} catch (NoResultException nre) {
			entity = null;
		}
		if (entity == null) {
			return Response.status(Status.NOT_FOUND).build();
		}
		return Response.ok(entity).build();
	}

	@GET
	@Produces("application/json")
	public List<Customer> listAll(@QueryParam("start") Integer startPosition,
			@QueryParam("max") Integer maxResult) {
		TypedQuery<Customer> findAllQuery = em.createQuery(
				"SELECT DISTINCT c FROM Customer c ORDER BY c.id",
				Customer.class);
		if (startPosition != null) {
			findAllQuery.setFirstResult(startPosition);
		}
		if (maxResult != null) {
			findAllQuery.setMaxResults(maxResult);
		}
		final List<Customer> results = findAllQuery.getResultList();
		return results;
	}

	@PUT
	@Path("/{id:[0-9][0-9]*}")
	@Consumes("application/json")
	public Response update(@PathParam("id") Long id, Customer entity) {
		if (entity == null) {
			return Response.status(Status.BAD_REQUEST).build();
		}
		if (id == null) {
			return Response.status(Status.BAD_REQUEST).build();
		}
		if (!id.equals(entity.getId())) {
			return Response.status(Status.CONFLICT).entity(entity).build();
		}
		if (em.find(Customer.class, id) == null) {
			return Response.status(Status.NOT_FOUND).build();
		}
		try {
			entity = em.merge(entity);
		} catch (OptimisticLockException e) {
			return Response.status(Response.Status.CONFLICT)
					.entity(e.getEntity()).build();
		}

		return Response.noContent().build();
	}
}

Now our Forge project is complete. We will add to the pom.xml the WildFly plugin so that we can deploy using our Maven project:

<plugins>
	<plugin>
	        <groupId>org.wildfly.plugins</groupId>
	        <artifactId>wildfly-maven-plugin</artifactId>
	        <version>1.0.2.Final</version>
	        <configuration>
	            <filename>${project.build.finalName}.war</filename>
	        </configuration>
	</plugin>
</plugins>

That’s all. Build and deploy with:

mvn wildfly:deploy

Testing the REST Application

In order to test our application, we will use the cURL utility to send JSON content type. This is needed to create new Entities:

curl -d '{  "firstname": "john",  "surname": "doe",  "email": "johndoeEgmail.com"}' -H "Content-Type: application/json" -X POST http://localhost:8080/demo-rest/rest/customers

curl -d '{  "firstname": "uncle",  "surname": "tom",  "email": "uncletomEgmail.com"}' -H "Content-Type: application/json" -X POST http://localhost:8080/demo-rest/rest/customers

You can check the list of Customers or individual Customers with:

curl http://localhost:8080/demo-rest/rest/customers/

curl http://localhost:8080/demo-rest/rest/customers/1

Finally, you can delete one Customer using:

curl -X DELETE http://localhost:8080/demo-rest/rest/customers/2

That’s all! Enjoy the screencast of it!

{source}
<script src=”https://asciinema.org/a/q6iSwtgOUmScqgOZ7V7W6Th8A.js” id=”asciicast-q6iSwtgOUmScqgOZ7V7W6Th8A” async></script>
{/source}

Using JAX-RS Server Sent Events with WildFly

JAX-RS 2.1 (JSR 370) contains several enhancements like Server Sent Events which will be discussed in this tutorial.

Here are in a nutshell the key enhancements provided by JAX-RS 2.1 :

  • Support for Server Sent Events (server and client)
  • JSON Binding (JSON-B) API integration

We have already discussed about JSON Binding (JSON-B) in this tutorial How to use JSON-B for parsing Java objects to/from JSON  so we will focus on the first item.

JAX-RS Support for Server Sent Events

Server Sent Events standard (SSE) are text/events which are streamed from the Server to the Client. JAX-RS includes both a Server API for publishing the SSE and a Client API for consuming them. Let’s see quickly an example of it:

@GET 
@Produces(MediaType.SERVER_SENT_EVENTS)
public void sendEvent(@Context SseEventSink eventSink, @Context Sse sse) {  
	OutboundSseEvent sseEvent = sse.newEvent("Today is ", new java.util.Date().toString());  
	eventSink.send(sseEvent);  
	eventSink.close();  
}

There are several key elements in the above Rest service:

  • The generic interface javax.ws.rs.sse.SseEvent represents a generic abstraction for a Server Sent Event and it’s implemented by the InboundSseEvent and OutboundSseEvent representing incoming and outgoing events respectively.
  • In the above example we are sending a single event containing a text with the current Date.
  • The actual event streaming is done by the SseEventSink interface which can send either single events or broadcast events to subscribers.

Coding a full Server-Client SSE example

Let’s create a full example which will deliver some server-events like generated random numbers to the Client. In first instance, we will use a very simple javascript client, then we will move to a Java client.

Here is our server service:

package com.itbuzzpress.chapter11;

import java.util.List;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import javax.annotation.Resource;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.sse.OutboundSseEvent;
import javax.ws.rs.sse.Sse;
import javax.ws.rs.sse.SseEventSink;


@Path("/events")
public class SseResource {

 
	Executor executorService = Executors.newSingleThreadExecutor();

	@GET
	@Produces(MediaType.SERVER_SENT_EVENTS)
	public void sendEvents(@Context SseEventSink sseEventSink, @Context Sse sse) {
		Random rnd = new Random();
		IntStream rndStream = IntStream.generate(() -> rnd.nextInt(90));
		List<Integer> lottery = rndStream.limit(5).boxed().collect(Collectors.toList());

		executorService.execute(() -> {
			lottery.forEach(value -> {
				try {
					TimeUnit.SECONDS.sleep(5);
					System.out.println("Sending the following value: " + value);
					final OutboundSseEvent outboundSseEvent = sse.newEventBuilder().name("lottery")
							.data(Integer.class, value).build();
					sseEventSink.send(outboundSseEvent);
				} catch (InterruptedException ex) {
					ex.printStackTrace();
				}

			});
			sseEventSink.close();
		});

	}
 
}

As you can see, the above code is just slightly more complex as it use the java.util.stream API to generate a set of Random numbers which are placed in a Collection. Then the events are delivered through the sendEvents method which produces MediaType.SERVER_SENT_EVENTS. The Collection is cycled every 5 seconds and the Random number is sent to the Client using the SseEventSink.

Now let’s move to the Client. At first we will use a minimal javascript client which uses the EventSource interface:

 <body onload="getRandomValues()">
  . . .
        <script>
            function getRandomValues() {
                var source = new EventSource("rest/events/");
                source.addEventListener('lottery', function (event) {
                    document.getElementById("number").innerHTML = event.data;
                }, false);
            }
        </script>
 </body>

The EventSource interface is a Javascript interface to server-sent events. An EventSource instance opens a persistent connection to an HTTP server, which sends events in text/event-stream format. The connection remains open until closed by calling EventSource.close().

Unlike WebSockets, server-sent events are unidirectional; that is, data messages are delivered in one direction, from the server to the client. That makes them an excellent choice when there’s no need to send data from the client to the server in message form.

By deploying the application will print on the screen Random numbers delivered by the Server side event

Coding a Java Client

The following example is a Java client which is embedded in an EJB Timer. As soon as the Singleton EJB is deployed, the WebTarget is linked to the REST Service. Then, Timer object is created to register and read event data from the Server Side Event.

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

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.ejb.Schedule;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.sse.SseEventSource;

@Singleton
@Startup
public class SSEClient {
	Client sseClient;
	WebTarget target;
	SseEventSource eventSource;
	@Resource 
	TimerService timerService;

	public ArrayList<String> listUsers;

	@PostConstruct
	public void init() {

		this.sseClient = ClientBuilder.newClient();
		this.target = this.sseClient.target("http://localhost:8080/jaxrs21sse-1.0/rest/events");

		timerService.createSingleActionTimer(5000, new TimerConfig());
		System.out.println("SSE client timer created");

		// Server side event source 
		eventSource = SseEventSource.target(target).build();
		System.out.println("SSE Event source created........");

	}

	public void addUser(String username) {
		listUsers.add(username);
	}

	public void removeUser(String username) {
		listUsers.remove(username);
	}

	public ArrayList<String> getListUsers() {
		return listUsers;
	}

	@Timeout
	public void client() {

		try {
			eventSource.register((sseEvent) -> {
				System.out.println("SSE event recieved ----- " + sseEvent.readData());
			}, (e) -> e.printStackTrace());

			eventSource.open();

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	@PreDestroy
	public void close() {
		eventSource.close();
		System.out.println("Closed SSE Event source..");
		sseClient.close();
		System.out.println("Closed JAX-RS client..");
	}

}

Basically once SseEventSource is created and connected to a server, registered event consumer will be invoked when an inbound event arrives. In case of errors, an exception will be passed to a registered consumer so that it can be processed.

SSE Broadcasting

Another API which is worth mentioning is the javax.ws.rs.sse.SseBroadcaster. By using this interface, SSE events can be broadcasted to multiple clients simultaneously. It will iterate over all registered SseEventSinks and send events to all requested SSE Stream. An application can create a SseBroadcaster from an injected context Sse. The broadcast method on a SseBroadcaster is used to send SSE events to all registered clients. The following code snippet is an example on how to create SseBroadcaster, subscribe and broadcast events to all subscribed consumers:

   @GET
   @Path("/subscribe")
   @Produces(MediaType.SERVER_SENT_EVENTS)
   public void subscribe(@Context SseEventSink sseEventSink) throws IOException
   {
      if (sseEventSink == null)
      {
         throw new IllegalStateException("No client connected.");
      }
      if (sseBroadcaster == null)
      {
         sseBroadcaster = sse.newBroadcaster();
      }
      sseBroadcaster.register(sseEventSink);
   }

   @POST
   @Path("/broadcast")
   public void broadcast(String message) throws IOException
   {
      if (sseBroadcaster == null)
      {
         sseBroadcaster = sse.newBroadcaster();
      }
      sseBroadcaster.broadcast(sse.newEvent(message));

   }          

The source code for the JAX-RS Server Sent Event Demo is available on Github at: https://github.com/fmarchioni/practical-javaee7-development-wildfly/tree/javaee8/code/chapter11/jaxrs-sse

Swagger quickstart tutorial

Swagger is a popular specification for REST APIs which can be used for a variety of purposes such as:

  • Generating an interactive API console to quickly learn about and try the API.
  • Generating the client SDK code needed for implementations on various platforms.

In order to integrate Swagger with your Web application you need to following three steps:

  • You need to marge the User Interface components from swagger-ui project
  • Apply the Swagger API annotations in your REST Service
  • Bootstrap Swagger in your Web server

Merging Swagger UI in your Web project

The Swagger UI Project is available at the following URL: https://github.com/swagger-api/swagger-ui

What you need to do in a nutshell is copying the content of the dist folder of swagger-ui into your project’s webapp folder. Once done with it, pick up the welcome page, named index.html and edit the file to specify the url of your Swagger service, for example:

      if (url && url.length > 1) {
        url = decodeURIComponent(url[1]);
      } else {
        url = "http://localhost:8080/swagger-demo/api/swagger.json";
      }

Applying Swagger API annotations

Done with merging your UserInterface let’s move to the REST Service.

package com.mastertheboss;


import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/simple")
@Api(value = "SimpleRESTService", description = "Swagger Demo")
public class SimpleRESTService {
	@GET
	@Path("/text")
	@ApiOperation(value = "Hello World Swagger")
	public String getHello () 
	{
		return "hello world!";
	} 
	@GET
	@Path("/json")
	@ApiOperation(value = "Returns param", notes = "Returns param", response = SimpleProperty.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successful retrieval of param value", response = SimpleProperty.class)})
	@Produces(MediaType.APPLICATION_JSON)
	public SimpleProperty getPropertyJSON () 
	{
        SimpleProperty p = new SimpleProperty("key","value");
		return p;
	}
	@GET
	@Path("/xml")
 
	@ApiOperation(value = "Returns param", notes = "Returns param", response = SimpleProperty.class)
    @ApiResponses(value = {
        @ApiResponse(code = 200, message = "Successful retrieval of param value", response = SimpleProperty.class)})
	@Produces(MediaType.APPLICATION_XML)
	public SimpleProperty getPropertyXML () 
	{
        SimpleProperty p = new SimpleProperty("key","value");
		return p;
	}
}

Aside from standard JAX-RS annotations, two Swagger core annotations have been included in your REST Service:

  • @io.swagger.annotations.Api Which marks a class as a Swagger resource. By default, Swagger-Core will only include and introspect only classes that are annotated with @Api and will ignore other resources
  • @io.swagger.annotations.ApiOperation Which describes an operation or typically a HTTP method against a specific path.

Finally you need to bootstrap Swagger core engine in order to be able to run its Api. For the sake of simplicity, we will use the Application class to bootstrap your Swagger core. If you need an higher level of flexibility we suggest having a look at the Spring configuration at the following link: https://github.com/swagger-api/swagger-core/wiki/Swagger-Core-RESTEasy-2.X-Project-Setup

package com.mastertheboss;

import io.swagger.jaxrs.config.BeanConfig;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
 

@ApplicationPath("/api")
public class Configurator extends Application {

 
    public Configurator() {
        init();
    }
 

    private void init() {
      
    	  BeanConfig beanConfig = new BeanConfig();
          beanConfig.setVersion("1.0.0");
          beanConfig.setSchemes(new String[]{"http"});
          beanConfig.setHost("localhost:8080");
          beanConfig.setBasePath("/swagger-demo/api");
          beanConfig.setResourcePackage(SimpleRESTService.class.getPackage().getName());
          beanConfig.setTitle("RESTEasy, Swagger and Swagger UI Example");
          beanConfig.setDescription("Sample RESTful API built using RESTEasy, Swagger and Swagger UI");
          beanConfig.setScan(true);
    }
}

That’s all. Now build and deploy your Web application on WildFly and surf on the home page of it. As you can see from the following picture an interactive API console is available for every operation:

If you expand each operation you will be able to learn more about it and eventually test the operation:

You can checkout for the source code of this example at: https://github.com/fmarchioni/mastertheboss/tree/master/swagger-demo

RESTEasy basic authentication example

In this tutorial we will demonstrate how to use a BASIC kind of authentication in your REST Services using RESTEasy on the backend and the DefaultHttpClient on the client side.

First of all we will define one user on the application server that belongs to a Role. If you want to rely on the “other” Security domain (default) it’s enough to execute the add-user.sh /add-user.cmd script which is available in the JBOSS_HOME/bin folder.

What type of user do you wish to add?

 a) Management User (mgmt-users.properties)

 b) Application User (application-users.properties)

(a): b

 

Enter the details of the new user to add.

Using realm ‘ApplicationRealm’ as discovered from the existing property files.

Username : jboss

Password :

Re-enter Password :

What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[  ]: Manager

About to add user ‘jboss’ for realm ‘ApplicationRealm’

Is this correct yes/no? yes

In this example, we have created the “jboss” user that belongs to the “Manager” group.

Done with the user, we will add to our REST Web service application the Security Constraints so that all of our services will be available only to the Manager user:

<security-constraint>
	<web-resource-collection>
		<web-resource-name>HtmlAuth</web-resource-name>
		<description>application security constraints</description>
		<url-pattern>/*</url-pattern>
		<http-method>GET</http-method>
		<http-method>POST</http-method>
	</web-resource-collection>
	<auth-constraint>
		<role-name>Manager</role-name>
	</auth-constraint>
</security-constraint>
<login-config>
	<auth-method>BASIC</auth-method>
	<realm-name>UserRoles simple realm</realm-name>
</login-config>
<security-role>
	<role-name>Manager</role-name>
</security-role>

 Finally, we will specify that the web application uses the “other” Security Domain in the jboss-web.xml file:

<jboss-web>
    <security-domain>java:/jaas/other</security-domain>
</jboss-web>

 Note: This step can be skipped as the “other ” Security Domain is tested if you don’t provide any. However it would be needed if you are using another kind of Security Domain

That’s all. Now your REST Service will request a BASIC browser authentication when invoked.

Configuring the Client side

The simplest way to write a Client aware of Basic Authentication is by means of the org.apache.http.impl.client.DefaultHttpClient which includes a CredentialsProvider interface for setting Base64 username and password.

Here is the code:

String BASE_URL = "http://localhost:8080/myapp/rest/search/125";

DefaultHttpClient client = new DefaultHttpClient();

client.getCredentialsProvider().setCredentials(
		new AuthScope("localhost", 8080),
		new UsernamePasswordCredentials("jboss", "Password1!"));

HttpGet httppost = new HttpGet(BASE_URL);

System.out.println("executing request " + httppost.getRequestLine());
HttpResponse response = null;
try {
	response = client.execute(httppost);
	BufferedReader br = new BufferedReader(new InputStreamReader(
			(response.getEntity().getContent())));

	String output;
	System.out.println("Output from Server .... \n");
	while ((output = br.readLine()) != null) {
		System.out.println(output);
	}

	client.getConnectionManager().shutdown();
} catch (ClientProtocolException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
} catch (IOException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
}

Basically this code is executing an HTTP GET toward a REST Service and consuming the output as a BufferedReader Stream. As the REST Service is producing a JSON list of objects:

@GET
@Path("/search/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response updateBusta(@PathParam("id")) {
	
	// Produce JSON 
	return Response.status(200).build();

}

 Then the output of the Client application will be the JSON String returned from the Server:

[{“_id”:{“date”:1423039138000,”time”:1423039138000,”timestamp”:1423039138,”machine”:296272174,”new”:false,”inc”:-919141096,”timeSecond”:1423039138},”nome”:”101″,”datacreazione”:1423039138558,”anno”:2015,”pec”:”m.francesco@gmail.com”,”oggetto”:”012001″,”firma”:false,”deposito”:false,”files”:[“Amazon.pdf”,”Cartel1.pdf”],”fileTypes”:[“AP”,”SM”]}

 

REST services using JavaFX Tutorial

This tutorial shows how to invoke REST services using JavaFX API. For the purpose of it, we will use the KitchenSink basic REST service
and engineer a JavaFX client for it.

A pre-requisite of this tutorial is that you have deployed the Kitchensink basic demo application which allows you to create a compliant Java EE 6 application using a mix of different technologies. You can download the Maven project from here: https://github.com/jboss-jdf/jboss-as-quickstart/tree/master/kitchensink

The next requisite is that you have got installed JavaFx on your machine: this is automatically included if you have got Java SE 1.7. If you are running a Java 1.6 platform, you have to download JavaFX separately http://www.oracle.com/technetwork/java/javafx/downloads/index.html

So let’s move to the code. This application is made up of two core classes Main.java which contains the GUI and registers the form action handlers and MemberController.java which executes the REST Calls using RESTEasy client API and modifies the model accordingly.

Here’s Main.java

public class Main extends Application implements EventHandler<ActionEvent>,
        ServerResponseHandler {

    private TextField txtName;
    private TextField txtEmail;
    private TextField txtPhone;

    // Labels with the error messages
    private Label lblMessageName;
    private Label lblMessageEmail;
    private Label lblMessagePhoneNumber;
    private Messages messages;

    private TableView<Member> tableView;

    private Pane mainPane;
    private TitledPane errorPane;
    private ProgressIndicator progressIndicator;

    private final String MAIN_CSS = getClass().getResource(
            "/resources/css/main.css").toString();

    MemberController controller = new MemberController();

    public static void main(String[] args) {
          
        launch(args);
    }

    @Override
    public void start(Stage palco) throws Exception {

        List<String> parameters = getParameters().getUnnamed();
        if (parameters.size() > 0) {
            String url = parameters.get(0);
            System.out.println(url);
        }

        StackPane root = new StackPane();
        root.getChildren().addAll(createMainPane(), createProgressIndicator(),
                createErrorPane());
        palco.setScene(SceneBuilder.create().width(500).height(400).root(root)
                .stylesheets(MAIN_CSS).build());
        palco.setTitle("KitchensinkFX");
        palco.setResizable(false);
        palco.show();
        updateData();
    }

    private Pane createMainPane() {
        return mainPane = VBoxBuilder.create()
                .children(createNewMemberForm(), createListMembersPane())
                .build();
    }

    private ProgressIndicator createProgressIndicator() {
        progressIndicator = ProgressIndicatorBuilder.create().maxWidth(100)
                .visible(false).maxHeight(100).build();
        progressIndicator.visibleProperty().bindBidirectional(
                mainPane.disableProperty());
        return progressIndicator;
    }

    private TitledPane createErrorPane() {
        errorPane = TitledPaneBuilder
                .create()
                .text("Error on server communication!")
                .visible(false)
                .prefHeight(150)
                .maxWidth(250)
                .content(
                        LabelBuilder
                                .create()
                                .wrapText(true)
                                .maxWidth(200)
                                .styleClass("error-label", "panel-error-label ")
                                .build()).collapsible(false).build();
        errorPane.setOnMouseClicked(new EventHandler<Event>() {
            public void handle(Event arg0) {
                errorPane.setVisible(false);
            }
        });
        mainPane.visibleProperty().bind(errorPane.visibleProperty().not());
        return errorPane;
    }

    private Node createNewMemberForm() {
        GridPane newMemberForm = GridPaneBuilder.create().vgap(10).hgap(5)
                .translateX(10).translateY(10).prefHeight(300).build();
        newMemberForm.add(new Label("Name"), 0, 0);
        newMemberForm.add(txtName = new TextField(), 1, 0);
        newMemberForm.add(
                lblMessageName = LabelBuilder.create()
                        .styleClass("error-label").maxWidth(250).build(), 2, 0);

        newMemberForm.add(new Label("Email"), 0, 1);
        newMemberForm.add(txtEmail = new TextField(), 1, 1);
        newMemberForm.add(
                lblMessageEmail = LabelBuilder.create()
                        .styleClass("error-label").maxWidth(250).build(), 2, 1);

        newMemberForm.add(new Label("Phone"), 0, 2);
        newMemberForm.add(txtPhone = new TextField(), 1, 2);
        newMemberForm.add(lblMessagePhoneNumber = LabelBuilder.create()
                .styleClass("error-label").maxWidth(250).build(), 2, 2);

        newMemberForm.add(
                ButtonBuilder.create().text("Add Member").onAction(this)
                        .build(), 1, 3);
        return newMemberForm;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private Node createListMembersPane() {
        tableView = new TableView<Member>();

        TableColumn emailColumn = new TableColumn<Member, String>();
        emailColumn.setCellValueFactory(new PropertyValueFactory("email"));
        TableColumn nameColumn = new TableColumn();
        nameColumn.setCellValueFactory(new PropertyValueFactory("name"));
        TableColumn phoneColumn = new TableColumn();
        phoneColumn
                .setCellValueFactory(new PropertyValueFactory("phoneNumber"));

        emailColumn.setText("Email");
        nameColumn.setText("Name");
        phoneColumn.setText("Phone Number");
        phoneColumn.setPrefWidth(150);
        tableView.getColumns().setAll(nameColumn, emailColumn, phoneColumn);
        return tableView;
    }

    private void updateData() {
        progressIndicator.setVisible(true);
        controller.getAllMembers(this);
    }
    private void clearMessagesAndFields() {
        lblMessageEmail.setText("");
        lblMessageName.setText("");
        lblMessagePhoneNumber.setText("");

        txtEmail.setText("");
        txtName.setText("");
        txtPhone.setText("");
    }

    private void showMessages() {
        lblMessageEmail.setText(messages.getEmail());
        lblMessageName.setText(messages.getName());
        lblMessagePhoneNumber.setText(messages.getPhoneNumber());
    }

    private void showErrorMessage(String errorMessage) {
        ((Label) errorPane.getContent()).setText(errorMessage);
        errorPane.setVisible(true);
    }

    public void onMembersRetrieve(final List<Member> members) {
        Platform.runLater(new Runnable() {
            public void run() {

                tableView.setItems(FXCollections.observableList(members));
                progressIndicator.setVisible(false);
            }
        });
    }

    public void onMemberCreation(final Messages serverMessages) {
        Platform.runLater(new Runnable() {
            public void run() {
                messages = serverMessages;
                if (serverMessages == null) {
                    clearMessagesAndFields();
                    updateData();
                } else {
                    showMessages();
                }
                progressIndicator.setVisible(false);
            }
        });
    }

    public void onServerError(final Exception e) {
        Platform.runLater(new Runnable() {
            public void run() {
                showErrorMessage(e.getMessage());
                e.printStackTrace();
                progressIndicator.setVisible(false);
            }
        });
    }

    public void handle(ActionEvent evt) {
        progressIndicator.setVisible(true);
        controller.createMember(
                new Member(txtName.getText(), txtEmail.getText(), txtPhone
                        .getText()), this);
    }
}

We will not enter into the details of GUI creation; we will just draw your attention on the handle method, which is the one that is invoked when you click on the Register button and invokes the Controller’s create method passing the text fields and a reference to the ServerResponseHandler interface that is implemented by the class:

The ServerResponseHandler is used to handle the server responses when you want to make a non thread locking request

public interface ServerResponseHandler {
    public void onMembersRetrieve(List<Member> members);

    public void onMemberCreation(Messages messages);
    
    public void onServerError(Exception e);
}

Then the MemberController class is responsible to invoke the Kitchensink REST members service (available at http://localhost:8080/jboss-as-kitchensink/rest/members ) which returns the list of registered members using JSON.

public class MemberController {
    /**
     * The Base Url for the service
     */
    private String applicationUrl = "http://localhost:8080/jboss-as-kitchensink/rest/members";

    /**
     * 
     * It simply makes an HTTP request to create a Member
     * 
     * @param member
     */
    @SuppressWarnings("unchecked")
    public Messages createMember(Member member) {
        ClientRequest cr = new ClientRequest(applicationUrl);
        cr.body(MediaType.APPLICATION_JSON, member);
        try {
            return (Messages) cr.post().getEntity(Messages.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void createMember(final Member member,
            final ServerResponseHandler serverResponseHandler) {
        Thread t = new Thread(new Runnable() {
            public void run() {
                try {
                    serverResponseHandler
                            .onMemberCreation(createMember(member));
                } catch (Exception e) {
                    serverResponseHandler.onServerError(e);
                }
            }
        });
        t.start();
    }

    @SuppressWarnings("unchecked")
    public List<Member> getAllMembers() throws Exception {
        ClientRequest cr = new ClientRequest(applicationUrl);
        cr.accept(MediaType.APPLICATION_JSON);
        return (List<Member>) cr.get().getEntity(
                new GenericType<List<Member>>() {
                });
    }

    public void getAllMembers(final ServerResponseHandler serverResponseHandler) {
        Thread t = new Thread(new Runnable() {
            public void run() {
                try {
                    serverResponseHandler.onMembersRetrieve(getAllMembers());
                } catch (Exception e) {
                    serverResponseHandler.onServerError(e);
                }
            }
        });
        t.start();
    }
}

As you can see, the actual invocation is performed into the public Messages createMember(Member member), which uses the org.jboss.resteasy.client.ClientRequest class to perform an HTTP Request. This method returns a Message object which is barely a wrapper for the Member we are trying to register.


Running the Java FX application

The full project is hosted at this fork of JBoss developer framework: https://github.com/fmarchioni/jboss-as-quickstart/tree/master/kitchensink-javafx

Before running the maven project, you have to check for the correct javafx-path, which is in the pom.xml

<javafx-path>C:\Java\jdk1.7.0_09\jre\lib\jfxrt.jar</javafx-path>

Launch the main application using

mvn install -Prun

Which will invoke the “run” profile that in turn will run the project’s Main class.

Here’s how the main form looks like:

Notice how validation error messages are beautifully handled in the Main class’ errorPane:

If you pass all validation checks, the new Member is finally added into JavaFX javafx.scene.control.TableColumn, just like it does in the KitchenSink dataTable component:

Enjoy JavaFX and RESTeasy services !

RESTful Web services on JBoss AS 7

In this sample tutorial we will show how to run RESTful Web services on JBoss AS 7 (release 7.0.2) using CDI and JPA to load persistent data.

In order to use Web services in the new application server release you need the following extension in your configuration file:

<extension module="org.jboss.as.webservices"/>

This extension is included by default since JBoss AS 7.1.1. In this example we will build a sample application which uses a RESTful Web services to gather some data which is returned in XML format. The first piece of our application will be the RESTful Web service which produces a list of Entities (of type Book):

package com.webservice;

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

import javax.persistence.Query;
import javax.ws.rs.*;
 

@Path("/list")
public class RESTService  {
 
@Inject
private EntityManager em;    
 
@GET
@Produces("text/xml")
 public List<Book> listUsers() {
 
   Query query = em.createQuery("FROM com.entity.Book");
   List <Book> list =  query.getResultList();

   return list;
 }
} 

In this example, we are binding the Path to the specific context named “/list”.
You can apply the @Path annotation also at method level so that you can specify different virtual paths for your web services.

Now Let’s add the Entity class named Book to our project, which simply maps the table named “Book” on the database:

package com.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@Entity
public class Book {

 @Id
 @Column(name = "name")
 String name;
 @Column(name = "isbn")
 String isbn;
 @Column(name = "price")
 int price;

public Book() {   }

public String getName() {
   return name;
}

public void setName(String name) {
   this.name = name;
}

public String getIsbn() {
   return isbn;
}

public void setIsbn(String isbn) {
   this.isbn = isbn;
}

public int getPrice() {
   return price;
}

public void setPrice(int price) {
   this.price = price;
}

}

Next thing, as we have seen, this project uses Context Dependency Injection to inject the EntityManager into our project. In order to do that, we need a simple qualifier which exposes an entity manager using the resource producer pattern :

package com.qualifier;

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

public class Resources {

 
 @SuppressWarnings("unused")
 @PersistenceContext
 @Produces
 private EntityManager em;

}

After that, in order to activate CDI, you need to place the beans.xml file into the WEB-INF folder of your application, so that the deployer triggers CDI deployment. As we are not defining any beans into this file, just place add an empty file named beans.xml under WEB-INF.


Now the last step is activating our Web Service. This can be done in two ways: using XML configuration:

1) Define in web.xml that your service is a REST Application

<servlet>
   <servlet-name>Resteasy</servlet-name>
     <servlet-class>
       org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
     </servlet-class>
     <init-param>
       <param-name>javax.ws.rs.Application</param-name> 
       <param-value>com.webservice.RESTService</param-value>
    </init-param>
 
 </servlet>  

2) Define a JAX-RS activator by adding a class which extends  javax.ws.rs.core.Application and defines an application path for your web service. [recommended]

package com.webservice;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/rest")
public class JaxRsActivator extends Application {
 
}

That’s all. Remember to add a persistence.xml file to your META-INF folder of your application

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
 xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
   <persistence-unit name="persistenceUnit" transaction-type="JTA">
     <provider>org.hibernate.ejb.HibernatePersistence</provider>
     <jta-data-source>java:/MySqlDS</jta-data-source>  
 
     <properties>
       <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
     </properties>
  </persistence-unit>
</persistence>

If you need a tutorial about installing a datasource on JBoss AS 7 you can find one here .

If you have correctly configured your application, you will end up with a project like this:

The RESTful web service will be accessible using the path: http://localhost:8080/restas7/rest/list