segunda-feira, 9 de novembro de 2015

Creating an Enterprise Application Repository with Intellij and WildFly


Note: unlike the other messages I wrote in this blog so far, this one is neither about Eclipse, nor authored by me. This tutorial is about IntelliJ, and was authored by João Ricardo Lourenço (on the photo), a student of Enterprise Application Integration.


I would like to thank him for this message. I hope that you find it useful.

This tutorial is an IntelliJ version of the one found in http://eai-course.blogspot.pt/2014/10/an-enterprise-application-repository-to.html. We will implement a complete Enterprise Application Repository, with a clear separation between frontend (Web), business logic (Enterprise Java Beans) and the data layer (using JPA with Wildfly/JBoss).
We will develop a single project that encompasses three different modules: The Web Project, the Enterprise Java Beans project and the JPA project. This will be done by incrementally adding features and functionality to an initial project. We will also configure a database datasource and add it to Wildfly.

Creating the Java EE Web Application

We start by creating the Java EE Web Application. This should create a simple “Hello World” webpage. To do this, we create a New project, select Java EE, and then  Web Application (Figure 1). You should have already configured JBoss as an Application Server. If you’re on Mac OSX and installed wildfly using the Homebrew package manager, you can find this directory by running brew info wildfly-as and seeing what is written under “The home of WildFly Application Server 9 is:”.
Figure 1 - Creating the Web Application Project
The project should now be created, and you should have an index.jsp file in your web subdirectory. If you run the default Run Configuration, JBoss should start and your browser should be directed to a URL similar to http://localhost:8080/enterprise_application_repository_war_exploded/. This is where the default artifact of the project is deployed within JBoss, and you’re just seeing the index.jsp of your project. Change something in index.jsp and then rerun the run configuration…notice how it changed in the webpage too?


Adding the JPA project/module

The next step is to add the JPA project on top of this.
Go to File -> New -> Module. Then, create a module with JPA support (Figure 2)
Figure 2 - Creating the JPA module

Be sure to select Hibernate!
Now you should have a new module. Within this module, a file persistence.xml should exist. This file links your JPA persistence units to Wildfly, and will have to be specially tailored to your needs. Mine is configured for a MySQL/MariaDB database and currently looks like the following:
persistence.xml
<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">

    <
persistence-unit name="Players" transaction-type="JTA">
        <
provider>org.hibernate.ejb.HibernatePersistence</provider>
        <
jta-data-source>java:/localMariaDB</jta-data-source>
        <
properties>
            <
property name="hibernate.show_sql" value="true"/>
            <
property name="hibernate.format_sql" value="true"/>
            <
property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
           
<property name="hibernate.hbm2ddl.auto" value="update"/>
            <
property name="hibernate.ddl-generation" value="create-tables" />
       
</properties>
    </
persistence-unit>
</
persistence>

You can copy and paste this file, but remember to also follow the next section of the tutorial so that you have the same datasource! What we’re doing in this file is specifying how the persistence-unit named Players is managed. We define that it uses the Hibernate JPA provider with the datasource (defined in Wildfly, see next section) named “java:/localMariaDB”. Next, we define several properties that make debug code prettier and ensure that Hibernate works, creating all the tables it needs. The information on how to access the database (database name, user and password) is configured within the datasource itself (see next section).
In this project (make sure you’re creating these files in the current module!), you can now create a package named data and copy and paste the content of the following two files. These represent the Player and Team entities.
Player.java

package data;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

/**
 * Entity implementation class for Entity: Player
 *
 */
@Entity
public class Player implements Serializable {
   
private static final long serialVersionUID = 1L;
   
@Id
    @GeneratedValue
(strategy=GenerationType.IDENTITY)
   
private int id;
   
private String name;
   
@Temporal(TemporalType.DATE)
   
private Date birth;
   
private float height;
   
@ManyToOne
   
private Team team;

   
public Player() {
       
super();
    }

   
public Player(String name, Date birth, float height, Team team) {
       
super();
       
this.name = name;
       
this.birth = birth;
       
this.height = height;
       
this.team = team;
    }

   
public String getName() {
       
return name;
    }

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

   
public Date getBirth() {
       
return birth;
    }

   
public void setBirth(Date birth) {
       
this.birth = birth;
    }

   
public float getHeight() {
       
return height;
    }

   
public void setHeight(float height) {
       
this.height = height;
    }

   
public Team getTeam() {
       
return team;
    }

   
public void setTeam(Team team) {
       
this.team = team;
    }

   
public static long getSerialversionuid() {
       
return serialVersionUID;
    }

   
@Override
   
public String toString() {
       
return this.name + " id = " + this.id + ", " + this.height + " plays for " + this.team.getName() + ". Born on " + this.birth;
    }

}

Team.java
package data;

import javax.persistence.*;
import java.io.Serializable;
import java.util.List;

@Entity
public class Team implements Serializable {
   
private static final long serialVersionUID = 1L;

   
@Id
    @GeneratedValue
(strategy=GenerationType.IDENTITY)
   
int id;
   
private String name;
   
private String address;
   
private String presidentname;
   
@OneToMany(mappedBy="team")
   
private List<Player> players;



   
public Team() {
       
super();
    }

   
public Team(String name, String address, String presidentname) {
       
super();
       
this.name = name;
       
this.address = address;
       
this.presidentname = presidentname;
    }

   
public String getName() {
       
return name;
    }
   
public void setName(String name) {
       
this.name = name;
    }
   
public String getAddress() {
       
return address;
    }
   
public void setAddress(String address) {
       
this.address = address;
    }
   
public String getPresidentname() {
       
return presidentname;
    }
   
public void setPresidentname(String presidentname) {
       
this.presidentname = presidentname;
    }
   
public List<Player> getPlayers() {
       
return players;
    }
   
public void setPlayers(List<Player> players) {
       
this.players = players;
    }
}

If you’ve done everything right, your project should be similar to what is shown in Figure 3. In the next steps, we’ll create the database that we referenced before, and also create the datasource in Wildfly.
Figure 3 - Structure of the project once the JPA module is configured


Creating the database

I’ll assume you have MySQL or MariaDB installed and running on its default port (should be the same mentioned in the persistence.xml file). Now, open a client session (run mysql) and run the following commands:
CREATE DATABASE testdb;
CREATE USER 'test'@'localhost' IDENTIFIED BY 'test';
GRANT ALL PRIVILEGES on *.* to 'test'@'localhost';
GRANT ALL PRIVILEGES on testdb.* to 'test'@'localhost';
FLUSH PRIVILEGES;

Quit this session. Your database should now be created and you should have a user identified by the username test and password test. Check that it works by trying to connect to this database using this username:
mysql -u test -p testdb
(Insert test as the password)
If the prompt is visible, all is well. The next step involves configuring the Wildfly datasource.


Configuring the Wildfly datasource

Remember the reference to “java:/localMariaDB” from earlier? We’re going to create this datasource now.
One thing you should understand is that, by default, when you hit “Run” in IntelliJ, it uses a different configuration than the one that is used when you manually open Wildfly (by running ./standalone.sh -c standalone-full.xml). Since you’re going to be testing the application inside IntelliJ, you should execute the next steps only after starting JBoss from within IntelliJ itself (click Run like you did before). If you want to have access to the same datasource outside of IntelliJ, you’ll have to repeat the exact same steps.
Wildfly needs to know how to connect to your database, so you should download the appropriate JDBC connector. For MySQL/MariaDB, you can get the latest jar from http://dev.mysql.com/downloads/connector/j/. You’re looking for something like mysql-connector-java-5.1.37-bin.jar.
Once you’ve got that jar, make sure that Wildfly/JBoss is running (hit “Run” in IntelliJ) and point your browser to http://localhost:9990. Login using the credentials you should have created before (using add-user.sh for administrative users). Now click Create Deployment (Figure 4). Then click Add, and select Upload a new deployment. Select the JAR you downloaded and continue. After that, make sure that the newly deployed jar is marked as enabled.
Figure 4 - Create deployment (adding the JDBC connector to WildFly)

It’s now time to really create the datasource. In the same page, go to Configuration -> Subsystems -> Datasources and click View (Figure 5). Click Add, select MySQL Datasource. Give it whatever name you wish, but make sure that the JNDI name matches the one found in the persistence.xml file (in our case, “java:/localMariaDB”). When selecting the Driver, switch to Detected Driver and select the first item (similar to mysql-connector-java-5.1.37-bin.jar_com.mysql.jdbc.Driver_5_1; if you don’t see any items, then you didn’t correctly deploy the JDBC connector or you didn’t enable it). In the next screen, set the database name (last part of the URL) to testdb (Your final URL should be something like jdbc:mysql://localhost:3306/testdb) and the username and password to test (these should match the ones in the previous step!!). Test the connection to make sure that it works.
 
Figure 5 - Select "View" to enter the Datasources page, where you can create the new datasource


Creating the EJB project/module

Now that JPA with hibernate should be working (we’ll test it later), it’s time to create the EJB project/module. Go to File -> New -> Module. Here, select “EJB: Enterprise Java Beans”. Don’t select “create ejb-jar.xml”! See Figure 6.
Figure 6
In this new module, create a package named ejb and add the following files (PlayersEJB and PlayersEJBRemote) to it. They contain the EJB interface and implementation.
PlayersEJB.java
package ejb;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import data.Player;
import data.Team;

/**
 * Session Bean implementation class PlayersEJB
 */
@Stateless
public class PlayersEJB implements PlayersEJBRemote {
   
@PersistenceContext(name="Players")
    EntityManager
em;

   
/**
     * Default constructor.
     */
   
public PlayersEJB() {
       
// TODO Auto-generated constructor stub
   
}

   
public static Date getDate(int day, int month, int year) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.
YEAR, year);
        cal.set(Calendar.
MONTH, month - 1);
        cal.set(Calendar.
DAY_OF_MONTH, day);

        Date d = cal.getTime();
       
return d;
    }


   
public void populate() {
        Team [] teams = {
new Team("Sporting", "Alvalade", "Carvalho"), new Team("Academica", "Coimbra", "Simões"), new Team("Porto", "Antas", "Costa"), new Team("Benfica", "Luz", "Vieira") };
        Player [] players = {
               
new Player("Albino", getDate(23,4,1987), 1.87f, teams[0]),
               
new Player("Bernardo", getDate(11,4,1987), 1.81f, teams[0]),
               
new Player("Cesar", getDate(12,5,1983), 1.74f, teams[0]),
               
new Player("Dionisio", getDate(3,12,1992), 1.67f, teams[0]),
               
new Player("Eduardo", getDate(31,8,1985), 1.89f, teams[0]),
               
new Player("Franco", getDate(6,1,1989), 1.95f, teams[1]),
               
new Player("Gil", getDate(7,12,1986), 1.8f, teams[1]),
               
new Player("Helder", getDate(14,5,1987), 1.81f, teams[1]),
               
new Player("Ilidio", getDate(13,6,1991), 1.82f, teams[1]),
               
new Player("Jacare", getDate(4,2,1993), 1.83f, teams[1]),
               
new Player("Leandro", getDate(4,10,1984), 1.81f, teams[2]),
               
new Player("Mauricio", getDate(3,6,1984), 1.8f, teams[2]),
               
new Player("Nilton", getDate(11,3,1985), 1.88f, teams[2]),
               
new Player("Oseias", getDate(23,11,1990), 1.74f, teams[2]),
               
new Player("Paulino", getDate(14,9,1986), 1.75f, teams[2]),
               
new Player("Quevedo", getDate(10,10,1987), 1.77f, teams[2]),
               
new Player("Renato", getDate(7,7,1991), 1.71f, teams[3]),
               
new Player("Saul", getDate(13,7,1992), 1.86f, teams[3]),
               
new Player("Telmo", getDate(4,1,1981), 1.88f, teams[3]),
               
new Player("Ulisses", getDate(29,8,1988), 1.84f, teams[3]),
               
new Player("Vasco", getDate(16,5,1988), 1.83f, teams[3]),
               
new Player("X", getDate(8,12,1990), 1.82f, teams[3]),
               
new Player("Ze", getDate(13,5,1987), 1.93f, teams[3]),
        };

       
for (Team t : teams)
           
em.persist(t);

       
for (Player p : players)
           
em.persist(p);
    }


   
public List<Player> playersTallerThan(float threshold) {
        System.
out.println("In the EJB. Height = " + threshold);
        Query q =
em.createQuery("from Player p where p.height >= :t");
        q.setParameter(
"t", threshold);
       
@SuppressWarnings("unchecked")
        List<Player> result = q.getResultList();
       
return result;
    }
}

PlayersEJBRemote.java
package ejb;

import java.util.List;

import javax.ejb.Remote;

import data.Player;

@Remote
public interface PlayersEJBRemote {
   
public void populate();
   
public List<Player> playersTallerThan(float threshold);
}

Note that after you’ve pasted, many errors should come up. You can solve this by doing ALT+ENTER on the errors and follow the recommended IntelliJ actions (Something similar to “Add library JPA 2.0-2.0 to classpath” and “Add dependency on module ‘jpa-module’”). It is crucial that you follow the “Add dependency” suggestion!
Your project is almost ready!


Using the EJB in the Web project

The EJB module is working, so go back to the original src folder in your project. It should be at the same level than your web folder. Create a package named servlet in it. Now create a PlayersTallerThan class within this package with the following content:
PlayersTallerThan.java
package servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import data.Player;
import ejb.PlayersEJBRemote;

/**
 * Servlet implementation class PlayersTallerThan
 */
// http://localhost:8080/ServerPlayers-Web/PlayersTallerThan?fill=1
// url = http://localhost:8080/ServerPlayers-Web/PlayersTallerThan?height=1.80
@WebServlet("/PlayersTallerThan")
public class PlayersTallerThan extends HttpServlet {
   
private static final long serialVersionUID = 1L;
   
@EJB
   
PlayersEJBRemote ejbremote;

   
/**
     * @see HttpServlet#HttpServlet()
     */
   
public PlayersTallerThan() {
       
super();
    }

   
/**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
   
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        response.setContentType(
"text/html");

       
if (request.getParameter("fill") != null) {
           
ejbremote.populate();
            out.println(
"<h1>Populate: OK!</h1>");
        }
       
else {
           
float tall = Float.parseFloat(request.getParameter("height"));
            List<Player> lp =
ejbremote.playersTallerThan(tall);

            out.println(
"<h1> Players </h1>");
           
for (Player p : lp)
                out.println(p +
"<br/>");
        }
    }

   
/**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
   
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

Once more, you can solve errors by hitting ALT+ENTER and performing actions (similar to) “Add dependency on module ‘ejb-module’” and “Add dependency on module ‘jpa-module’”. The names ejb-module and jpa-module are my module names.
Your current project should look like the one in Figure 7.
Figure 7 - This is what your project should look like after all the modules and files have been created

Bringing it all together!

Up until now, your only artifact (i.e. deployable module) has been the one from the original project. We’re going to merge every module into a single artifact.
Go to File -> Project Structure -> Artifacts. You will probably see two artifacts: one for the original project and another one for the EJB module. You can safely delete the EJB artifact. After doing that, click the original/other artifact, select “Build on Make”. Next, go to the right pane and add (double click) all “compile output” and resource files to the artifact (see Figure 8). Click Apply.
Figure 8 - Almost there! Double click the files on the right to add them as part of the final artifact

For the final touch, go to your Run configurations and edit the JBoss run configuration you’ve been using. Select Deployment, remove any artifacts there and add the one you configured in the previous step (Figure 9). Hit Apply. You should now be ready to go!
Figure 9 - Last step: select the artifact from the last step for deployment

Just to be safe, do a full Project Rebuild and then Run your project (the same  JBoss run configuration from before). If all goes well, your web browser should pop up pointing to a URL like localhost:8080/enterprise_application_repository_war_exploded/.
Add “PlayersTallerThan?fill=true” to the end of the URL, hit enter, and check if the output is “Populate: OK!”. If it is, then now replace the fill=true portion with height=1.0.

If there are no errors, then congratulations! You used your web application to invoke your injected EJB to fill the database using JPA and have now accessed the results of your hard work!