An open API service indexing awesome lists of open source software.

https://github.com/cedlemo/pochette

Just some JavaEE REST api tests
https://github.com/cedlemo/pochette

Last synced: 3 months ago
JSON representation

Just some JavaEE REST api tests

Awesome Lists containing this project

README

        

# Pochette

Little java web application to test Java EE + Mariadb + REST api + Ajax

* [Install Mariadb driver and test connection](#install-mariadb-driver-and-test-connection)
* [Create the DAL part](#create-the-dal-part)
* [Handle DAL exceptions](#handle-dal-exceptions)
* [Create a simple BLL](#create-a-simple-bll)
* [Configure the REST interface](#configure-the-rest-interface)
* [Install the dependencies](#install-the-dependencies)
* [Configure the REST path](#configure-the-rest-path)
* [Create a resource class](#create-a-resource-class)
* [Return xml answer](#return-xml-answer)
* [Support the JAXB specification](#support-the-jaxb-specification)
* [Link class serializable in xml](#link-class-serializable-in-xml)
* [LinksManagement methods return Response objects](#linksmanagemenent-methods-return-response-objects)
* [Return response in json format](#return-response-in-json-format)
* [Install jackson library](install-jackson-library)
* [Specify the new supported format in the LinksManagement methods](#specify-the-new-supported-format-in-the-linksmanagemenent-methods)

## Install Mariadb driver and test the connection.

see https://mariadb.com/kb/en/library/getting-started-with-the-java-connector/#using-the-jar-file-directly for reference.

```bash
mkdir -p ./WebContent/WEB-INF/lib
cd WebContent/WEB-INF/lib
wget https://downloads.mariadb.com/Connectors/java/connector-java-2.2.6/mariadb-java-client-2.2.6.jar
```

Add this library in the build path and add the JUnit 5 library.

the tests

```java
package fr.pochette.test;

import static org.junit.jupiter.api.Assertions.*;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.junit.jupiter.api.Test;

class MariaDBConnection {

@Test
void test() {
String url ="jdbc:mariadb://localhost/";
String user = "pochette_user";
String password = "pochette_password";
try (Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement(
//execute query
ResultSet rs = stmt.executeQuery("SELECT 'Hello World!'");
//position result to first
rs.first();
d>javax.xml.bind
jaxb-api
2.3.0

com.sun.xml.bind
jaxb-core
2.3.0

com.sun.xml.bind
jaxb-impl
2.3.0

javax.activation
activation
1.1.1
assertTrue("Hello World!".equals(rs.getString(1))){));
}
catch(SQLException e) {
fail("La connection a échouée : " + e.getLocalizedMessage());
}
}

}
```

## Create the DAL part

I only need for now to list all the links and see one link based on its id.

In the DAL package :
* DAOFactory.java

```java
package fr.pochette.dal;

public class DAOFactory {
public static LinkDAO getLinkDAO() {
return new LinkDaoMariaDBJdbcImpl();
}
}
```

* LinkDAO.java

```java
package fr.pochette.dal;

import java.util.List;

import fr.pochette.bo.Link;

public interface LinkDAO {
public List listAll() ;

public Link getLink(int id);
}
```

* LinkDaoMariaDBJdbcImpl.java

```java
package fr.pochette.dal;

import java.util.List;

import fr.pochette.bo.Link;

public class LinkDaoMariaDBJdbcImpl implements LinkDAO {

@Override
public List listAll() {
// TODO Auto-generated method stub
return null;
}

@Override
public Link getLink(int id) {
// TODO Auto-generated method stub
return null;
}
}
```

Add the code to generate the requests:

```java
public class LinkDaoMariaDBJdbcImpl implements LinkDAO {

public static final String SELECT_ALL_LINKS =
"SELECT idLink, title, url, creationDate, consumed, l.idType as l_idType, label FROM LINKS l"
+ " JOIN TYPES t ON l.idType = t.idType;";

public static final String SELECT_LINK_BY_ID=
"SELECT idLink, title, url, creationDate, consumed, l.idType as l_idType, label FROM LINKS l"
+ " JOIN TYPES t ON l.idType = t.idType WHERE idLink=?;";

@Override
public List listAll() {
List links = null;
try(Connection cnx = DBConnexionProvider.getConnection()){
links = _listAll(cnx);
} catch (SQLException e) {
e.printStackTrace();
}
return links;
}

public List _listAll(Connection cnx) throws SQLException {
List links = new ArrayList();
Statement st = cnx.createStatement();
ResultSet rs = st.executeQuery(SELECT_ALL_LINKS);
while(rs.next()) {
Link link = _buildLink(rs);
links.add(link);
}
st.close();
return links;
}

@Override
public Link getLink(int id) {
Link link = null;
try(Connection cnx = DBConnexionProvider.getConnection()) {
link = _getLink(cnx, id);
} catch(SQLException e) {
e.printStackTrace();
}
return link;
}

public Link _getLink(Connection cnx, int id) throws SQLException {
Link link = null;
PreparedStatement pstmt = cnx.prepareStatement(SELECT_LINK_BY_ID);
pstmt.setInt(1, id);
ResultSet rs = pstmt.executeQuery();
while(rs.next()) {
link = _buildLink(rs);
}
pstmt.close();
return link;
}

private Link _buildLink(ResultSet rs) throws SQLException {
LinkType linkType = new LinkType(rs.getInt("l_idType"), rs.getString("label"));
Link link = new Link(rs.getInt("idLink"),
rs.getString("title"),
rs.getString("url"),
rs.getDate("creationDate").toLocalDate(),
rs.getBoolean("consumed"),
linkType);
return link;
}
}
```

The database configuration is saved in the file *WebContent/META-INF/context.xml*.

```xml

```

## Handle DAL exceptions:

In order to handle all the exceptions, there will be one generic exception that
is used to catch all the dal exceptions via user defined error codes. Those
error codes will be later translated in readable message thanks to the
`ErrorMessageReader` class coupled with the *error_messages.properties* file.

* GenericException.java

```java
package fr.pochette.exception;

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

public class GenericException extends Exception{

private static final long serialVersionUID = 1L;

private List errorCodes;

public GenericException(){
this.errorCodes = new ArrayList();
}

public void addError(int code) {
this.errorCodes.add(code);
}

public List getErrorCodes(){
return this.errorCodes;
}

public boolean hasError() {
return this.errorCodes.size() > 0;
}
}

```

* ErrorMessageReader.java

```java
package fr.pochette.exception;

import java.util.ResourceBundle;

public class ErrorMessageReader {
private static ResourceBundle rb;

static {
try {
rb = ResourceBundle.getBundle("fr.pochette.exception.error_messages");
}
catch(Exception e)
{
e.printStackTrace();
}
}

public static String getMessage(int code)
{
String message="";
try
{
if(rb!=null)
{
message=rb.getString(String.valueOf(code));
} else {
message="Something went wrong with the file that contains the error messages";
}
} catch(Exception e) {
e.printStackTrace();
message="Unknown error";
}

return message;
}
}
```

## Create a simple BLL

In a package *fr.pochette.bll* the following file is added:

* LinkManager.java

```java
import java.util.List;

import fr.pochette.bo.Link;
import fr.pochette.dal.DAOFactory;
import fr.pochette.dal.LinkDAO;
import fr.pochette.exception.BusinessException;

public class LinkManager {
private LinkDAO linkDAO;

public LinkManager() {
this.linkDAO = DAOFactory.getLinkDAO();
}

public List listAll() throws BusinessException{
return this.linkDAO.listAll();
}

public Link getLink(int id) throws BusinessException {
return this.linkDAO.getLink(id);
}
}
```

## Configure the REST interface

### Install the dependencies

Now the project will be converted to a Maven project and in the *pom.xml* file
the implementation of the JAX-RS 2.0 specification will be specified so that
Maven will be able to download the needed jars.

* Right click on the Project > Configure > convert to Maven Project.
* Group Id : *Pochette*
* Artifact Id : *Pochette*
* Version : *0.0.1-SNAPSHOT*
* Packaging : *war*
* Pochette : *Pochette*

This will generate a pom.xml file in the root directory of the project. Now here
is the part to add in order to use the Apache CXF implementation.

```diff
diff --git a/pom.xml b/pom.xml
index 9b3e830..0eb5cc7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,6 +5,18 @@
0.0.1-SNAPSHOT
war
Pochette
+
+
+ org.apache.cxf
+ cxf-rt-frontend-jaxrs
+ 3.1.6
+
+
+ org.apache.cxf
+ cxf-rt-rs-http-sci
+ 3.1.6
+
+

src

```
If the JDK version used is >= 9 then it is necessary to add the following
dependencies ([source](https://stackoverflow.com/questions/46920461/java-lang-noclassdeffounderror-javax-activation-datasource-on-wsimport-intellij )):

```xml

javax.xml.bind
jaxb-api
2.3.0

com.sun.xml.bind
jaxb-core
2.3.0

com.sun.xml.bind
jaxb-impl
2.3.0

javax.activation
activation
1.1.1

```

All the dependencies will be downloaded after the following actions:
* right click on the project
* select Maven
* select Update Project

### Configure the REST path

In the package *fr.pochette.rest*, a new class is created:

```java
package fr.pochette.rest;

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

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

}
```

This class will be detected by Tomcat and will be used as a configuration for
the REST application.

### Create a resource class

Now that there is a class that defines the root path of the REST application,
I need to create a resource class that will manage the requests like GET, POST and so on
for the `Link` objects.

*LinksManagement*

```java
package fr.pochette.rest;

import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

import fr.pochette.bll.LinkManager;
import fr.pochette.bo.Link;
import fr.pochette.exception.BusinessException;

@Path("/links")
public class LinksManagement {

@GET
public String getLinks() {
LinkManager linkManager = new LinkManager();
String linkTitles = "";
try {
List links = linkManager.listAll();
for(Link l : links) {
linkTitles += l.getTitle() + "; " ;
}
} catch (BusinessException e) {
linkTitles = "An error occured " + e.getLocalizedMessage();
}
return linkTitles;
}
}
```

Now the connection with a browser to *http://localhost:8080/Pochette/rest/links*
returns :

```
Detecting use case of GADT with OCaml; Documentation OCaml; OCaml Mooc; Shahmen Poison; JJC De Mondoville Dominus Regnavit Mov. 4&5/6;
```

### Return xml answer

In order to be able to return xml anwsers, it is necessary to add support for the
JAXB specification, to make the `Link` class easily serializable in xml and to
make the `LinksManagement` methods return some usable Http responses.

#### Support for the JAXB specification

The JAXB APIs are considered to be Java EE APIs, and therefore are no longer contained on the default class path in Java SE 9. In Java 11 they are completely removed from the JDK.

ressources : [1](https://stackoverflow.com/questions/51564844/java-ee-rest-xml-support-javax-xml-bind-jaxbcontext-missing), [2](https://stackoverflow.com/questions/43574426/how-to-resolve-java-lang-noclassdeffounderror-javax-xml-bind-jaxbexception-in-j)

So if you have Java 6/7 or 8, nothing has to be done to have access to the JAXB api. For Java 9, you can modify the pom.xml file with the following maven dependencies.

```xml


javax.xml.bind
jaxb-api
2.3.0


com.sun.xml.bind
jaxb-impl
2.3.0


org.glassfish.jaxb
jaxb-runtime
2.3.0


javax.activation
activation
1.1.1

```

If the Java version is equal to 10, the quick and dirty way is to add the following
command-line option : `--add-modules java.xml.bind`.

In eclipse this can be done via:

* Right click on the project
* Run as
* Run Configurations
* Go on the Arguments panel
* In the Vm arguments entry add with a space char ` --add-modules java.xml.bind`

#### Link class serializable in xml

The two important things to note are:

* the `@XmlRootElement` annotation: it defines the xml element that describes any *Link* object.
* the presence of an empty constructor that is needed.

##### *src/fr/pochette/bo/Link.java*

```diff
diff --git a/src/fr/pochette/bo/Link.java b/src/fr/pochette/bo/Link.java
index 6fc94d5..b8293ef 100644
--- a/src/fr/pochette/bo/Link.java
+++ b/src/fr/pochette/bo/Link.java
@@ -1,8 +1,14 @@
package fr.pochette.bo;

import java.time.LocalDate;

-public class Link {
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name="link")
+public class Link {
int idLink;
String title;
String url;
@@ -82,6 +88,12 @@ public class Link {
public void setLinkType(LinkType linkType) {
this.linkType = linkType;
}
+ /**
+ *
+ */
public Link() {
+ super();
+ }
/**
* @param idLink
* @param title
@@ -99,7 +111,4 @@ public class Link {
this.setConsumed(consumed);
this.setLinkType(linkType);
}
-
-
-
}
```

#### LinksManagement methods return Response objects

Previously, what was returned was basic java String objects. Now the objects returned
are Response objects form *javax.rw.rs.core.Response*. This kind of object allows
to specify the status of the Http response, and the object to use to fill the
response body.

```java
package fr.pochette.rest;

import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.GenericEntity;

import fr.pochette.bll.LinkManager;
import fr.pochette.bo.Link;
import fr.pochette.exception.BusinessException;

@Path("/links")
public class LinksManagement {

@GET
@Produces(MediaType.APPLICATION_XML)
public Response getLinks() {
LinkManager linkManager = new LinkManager();
List links = null;
try {
links = linkManager.listAll();
} catch (BusinessException e) {
e.printStackTrace();
}
GenericEntity> resultat = new GenericEntity>(links) {};
return Response
.ok()
.entity(resultat)
.build();
}

@GET
@Path("/{id : \\d+}")
@Produces(MediaType.APPLICATION_XML)
public Response getLink(@PathParam("id") int id) {
LinkManager linkManager = new LinkManager();
Link link = null;
try {
link = linkManager.getLink(id);
} catch (BusinessException e) {
e.printStackTrace();
}
return Response
.ok()
.entity(link)
.build();
}
}
```

Now every access to *http://localhost:8080/Pochette/rest/links* or *http://localhost:8080/Pochette/rest/links/1* will return an xml response:

```xml

false

1

2
DOCUMENTATION

Documentation OCaml
http://caml.inria.fr/pub/docs/manual-ocaml/

```

### Return response in json format

#### Install jackson library

In order to be able to use the json format, we need the `jackson` library which
can be installed via the *pom.xml* file for maven.

```diff
diff --git a/pom.xml b/pom.xml
index 336abbb..edc9b72 100644
--- a/pom.xml
+++ b/pom.xml
@@ -37,6 +37,16 @@
javax.activation-api
1.2.0

+
+ org.codehaus.jackson
+ jackson-jaxrs
+ 1.9.13
+
+
+ org.codehaus.jackson
+ jackson-xc
+ 1.9.13
+


src
```

Then right click on the project in eclipse, click on Maven, Update Project and
the dependencies will be downloaded.

#### Specify the new supported format in the LinksManagement methods

The `@Produces` annotation supports an array of multiple `MediaType` :

```diff
diff --git a/src/fr/pochette/rest/LinksManagement.java b/src/fr/pochette/rest/LinksManagement.java
index 2300860..c759b58 100644
--- a/src/fr/pochette/rest/LinksManagement.java
+++ b/src/fr/pochette/rest/LinksManagement.java
@@ -18,7 +18,7 @@ import fr.pochette.exception.BusinessException;
public class LinksManagement {

@GET
- @Produces(MediaType.APPLICATION_XML)
+ @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getLinks() {
LinkManager linkManager = new LinkManager();
List links = null;
@@ -36,7 +36,7 @@ public class LinksManagement {

@GET
@Path("/{id : \\d+}")
- @Produces(MediaType.APPLICATION_XML)
+ @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getLink(@PathParam("id") int id) {
LinkManager linkManager = new LinkManager();
Link link = null;
```

#### Use Ajax to ask for the data in different formats

The problem now is to specify with format we need. For example every access with
the browser on *http://localhost:8080/Pochette/rest/links* will return data in the
xml format.

To solve this, there is Ajax. The following example, is a web page that contains
a button and two selectors. The selectors configure the format of the data that
will be requested.

* WebContent/listLinks.html

```html

Obtenir une réponse au format JSON




Select the format of the data


xml



json






function createXHR() {
if (window.XMLHttpRequest) // Objet standard
{
xhr = new XMLHttpRequest(); // Firefox, Safari, ...
}
else if (window.ActiveXObject) // Internet Explorer
{
xhr = new ActiveXObject("Msxml2.XMLHTTP");
}
return xhr;
}

function launchRequest()
{
var xhr = createXHR();
xhr.onreadystatechange = function()
{

if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
if(document.getElementById("xml").checked)
success(new XMLSerializer().serializeToString(xhr.responseXML));
else
success(xhr.responseText);
}
else
{
if(document.getElementById("xml").checked)
failure(xhr.status, xhr.responseXML);
else
failure(xhr.status, xhr.responseText);
}
}
};

xhr.open("GET", "/Pochette/rest/links", true);
if(document.getElementById("xml").checked)
xhr.setRequestHeader("Accept","application/xml");
else
xhr.setRequestHeader("Accept","application/json");
xhr.send(null);
}

function success(response)
{
document.getElementById("success").innerHTML=response;
document.getElementById("failure").innerHTML="";
}

function failure(codeResponse, response)
{
document.getElementById("failure").innerHTML=response;
document.getElementById("success").innerHTML="";
}

```

Note that the ajax requests are build here :

```javascript
xhr.open("GET", "/Pochette/rest/links", true);
if(document.getElementById("xml").checked)
xhr.setRequestHeader("Accept","application/xml");
else
xhr.setRequestHeader("Accept","application/json");
xhr.send(null);
```