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