GithubHelp home page GithubHelp logo

pochette's Introduction

Pochette

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

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.

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

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</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
   <groupId>com.sun.xml.bind</groupId>
   <artifactId>jaxb-impl</artifactId>
   <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>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
package fr.pochette.dal;

public class DAOFactory {
	public static LinkDAO getLinkDAO() {
		return new LinkDaoMariaDBJdbcImpl();
	}
}
  • LinkDAO.java
package fr.pochette.dal;

import java.util.List;

import fr.pochette.bo.Link;

public interface LinkDAO {
	public List<Link> listAll() ;

	public Link getLink(int id);
}
  • LinkDaoMariaDBJdbcImpl.java
package fr.pochette.dal;

import java.util.List;

import fr.pochette.bo.Link;

public class LinkDaoMariaDBJdbcImpl implements LinkDAO {

	@Override
	public List<Link> 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:

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<Link> listAll() {
        List<Link> links = null;
	try(Connection cnx = DBConnexionProvider.getConnection()){
            links = _listAll(cnx);
	} catch (SQLException e) {
            e.printStackTrace();
	}
	return links;
    }

    public List<Link> _listAll(Connection cnx) throws SQLException {
        List<Link> links = new ArrayList<Link>();
        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 version="1.0" encoding="UTF-8"?>
<Context>
	<Resource
		name="jdbc/pool_cnx"

		driverClassName="org.mariadb.jdbc.Driver"
		type="javax.sql.DataSource"

		url="jdbc:mariadb://localhost/POCHETTE_DB"
		username="pochette_user"
		password="pochette_password"

		maxTotal="100"
		maxIdle="30"
		maxWaitMillis="10000"
	/>

</Context>

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
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<Integer> errorCodes;

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

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

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

	public boolean hasError() {
		return this.errorCodes.size() > 0;
	}
}
  • ErrorMessageReader.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
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<Link> 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 --git a/pom.xml b/pom.xml
index 9b3e830..0eb5cc7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,6 +5,18 @@
   <version>0.0.1-SNAPSHOT</version>
   <packaging>war</packaging>
   <name>Pochette</name>
+  <dependencies>
+       <dependency>
+       <groupId>org.apache.cxf</groupId>
+       <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+       <version>3.1.6</version>
+       </dependency>
+        <dependency>
+       <groupId>org.apache.cxf</groupId>
+       <artifactId>cxf-rt-rs-http-sci</artifactId>
+       <version>3.1.6</version>
+       </dependency>
+  </dependencies>
   <build>
     <sourceDirectory>src</sourceDirectory>
     <resources>

If the JDK version used is >= 9 then it is necessary to add the following dependencies (source):

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
   <groupId>com.sun.xml.bind</groupId>
   <artifactId>jaxb-impl</artifactId>
   <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>

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:

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

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<Link> 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, 2

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.

<dependencies>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-impl</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jaxb</groupId>
        <artifactId>jaxb-runtime</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>javax.activation</groupId>
        <artifactId>activation</artifactId>
        <version>1.1.1</version>
    </dependency>
</dependencies>

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 --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.

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<Link> links = null;
   	try {
   		links = linkManager.listAll();
   	} catch (BusinessException e) {
   		e.printStackTrace();
   	}
   	GenericEntity<List<Link>> resultat = new GenericEntity<List<Link>>(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:

<link>
  <consumed>false</consumed>
  <creationDate/>
  <idLink>1</idLink>
  <linkType>
    <idType>2</idType>
    <label>DOCUMENTATION</label>
  </linkType>
  <title>Documentation OCaml</title>
  <url>http://caml.inria.fr/pub/docs/manual-ocaml/</url>
</link>

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 --git a/pom.xml b/pom.xml
index 336abbb..edc9b72 100644
--- a/pom.xml
+++ b/pom.xml
@@ -37,6 +37,16 @@
     <artifactId>javax.activation-api</artifactId>
     <version>1.2.0</version>
 </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-jaxrs</artifactId>
+ <version>1.9.13</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-xc</artifactId>
+ <version>1.9.13</version>
+ </dependency>
   </dependencies>
   <build>
     <sourceDirectory>src</sourceDirectory>

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 --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<Link> 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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Obtenir une réponse au format JSON</title>
</head>
<body>

	<div>
	<form >
	<fieldset>
	<legend>Select the format of the data</legend>
	<div>
		<input type="radio" checked id="xml" name="dataFormat">
		<label for="xml">xml</label>
	</div>
		<div>
		<input type="radio"  id="json" name="dataFormat">
		<label for="json">json</label>
	</div>
	</fieldset>
		<input type="button" value="Ask for links"  onclick="launchRequest()"/>
	</form>
	</div>

	<div id="success" style="color:green"></div>
	<div id="failure" style="color:red"></div>

	<script type="text/javascript">
	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="";
	}

	</script>
</body>
</html>

Note that the ajax requests are build here :

	    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);

pochette's People

Contributors

cedlemo avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.