Thursday, October 16, 2014

Java Persistence API with EclipseLink

(I created a new version of this exercise with Hibernate and Maven here)

In this message, I will provide a fully working example on Java Persistence API (JPA) using EclipseLink in a stand-alone program. I.e., this example can (and should) run outside any Application Server. Again, I am assuming that you are using Eclipse for Java EE developers.

The first thing you must do is to create a JPA project (change to Java EE or JPA view on the upper right corner of the Eclipse Window):




Let us call Players to our project, as we will store information of football teams on the database.


If you press "Next" a couple of times you'll end up having to chose the specific JPA implementation. EclipseLink might not be available on the first time. If that happens, you must install it, using the floppy disk image on the right:


We may now create the classes. In fact, we are going to create specially annotated classes that are Entities:


Let us call "Player" to the first one:

And now, it is time to program the Player Entity class. I will not take long explaining the details, as, with some effort, you may find explanations for all the annotations on the web. I will just say that many players can belong to the same Team, and therefore, we use the @ManyToOne annotation:
package data;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.*;

/**
 * Entity implementation class for Entity: Player
 *
 */
@Entity
public class Player implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy=GenerationType.AUTO)
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;
}
   
}

Let us now go for the Team Entity class, which has the converse annotation @OneToMany. This defines a bi-directional relation, which is to say, we may access the Team from the Player object, whereas accessing the Player from the Team object is also possible. The mappedBy indication serves to indicate that the Entity Player is the owner of the relation. In practice, the database table storing Player data will have an additional column to keep the identifier (foreign key) of the Team the player belongs to.



package data;

import java.io.Serializable;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Team implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy=GenerationType.AUTO)
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;
}

}



A few fundamental steps are missing. First, we need to create the database. I'm using MySQL. In my case, creating the database and granting permissions goes like this:


One of the most daunting tasks with JPA is to get the persistence.xml file right. This one works for me. Take care of using the right username and password. Maybe even the database, if it is not MySQL:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="Players">
<class>data.Player</class>
<class>data.Team</class>
<properties>
<property name="javax.persistence.jdbc.user" value="artur" />
<property name="javax.persistence.jdbc.password" value="****" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/PlayersAndTeams" />
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />

<property name="eclipselink.ddl-generation" value="create-tables" />
<property name="eclipselink.ddl-generation.output-mode"
value="database" />
</properties>

</persistence-unit>
</persistence>


Since we reference a JDBC driver, we need to include one, in a few steps:


Pick "Add External JARs..." and add a MySQL or other appropriate driver. You need to store it on your disk, maybe inside the Eclipse project:



Let us now write a program that stores actual data to the database. One should notice that the Team is inserted on the Player side, as the Player owns the relation (from my experience with my concrete choices of technologies, any attempt to do the opposite, entering the Player on the Team side, will result in losing all data):
package data;

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

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class WriteData {

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 static void main(String[] args) {
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]), 
};
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Players");
EntityManager em = emf.createEntityManager();
EntityTransaction trx = em.getTransaction();
trx.begin();
for (Team t : teams)
em.persist(t);
for (Player p : players)
em.persist(p);
trx.commit();
}

}


This program is very quiet. No output, or only a couple of log messages is a good sign.

Since we stored the data, we may now run a query to retrieve it. Notice that we are getting a team from the name, and then we print all the players of the team, despite never having inserted the data this way (we inserted it in the opposite direction):
package data;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;

public class ReadData {

public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Players");
EntityManager em = emf.createEntityManager();
Query q = em.createQuery("from Team t where t.name = :t");
q.setParameter("t", "Academica");
@SuppressWarnings("unchecked")
List<Team> resultteams = q.getResultList();
if (resultteams.size() > 0)
for (Player p : resultteams.get(0).getPlayers())
System.out.println(p);
}

}


The registered players of this outstanding team are:

Jacare id = 14, 1.83 plays for Academica. Born on Thu Feb 04 00:00:00 WET 1993
Ilidio id = 13, 1.82 plays for Academica. Born on Thu Jun 13 00:00:00 WEST 1991
Gil id = 11, 1.8 plays for Academica. Born on Sun Dec 07 00:00:00 WET 1986
Franco id = 10, 1.95 plays for Academica. Born on Fri Jan 06 00:00:00 WET 1989
Helder id = 12, 1.81 plays for Academica. Born on Thu May 14 00:00:00 WEST 1987

That's exactly what we inserted. If you got this result everything went well.

Finally, it is worthwhile to see how do the tables look like:



Monday, March 10, 2014

JNDI in WildFly

In a previous message of mine, I guided the reader through the process of creating Java Message Service applications. I provided a simple example with a sender and a receiver. This application worked for JBoss AS 7, but ceased to work in version 8/WildFly. One of the reasons for this lies on the JNDI server, which left port 4447 and moved to port 8080. The exact ports depend on the configuration, but the idea is that the JNDI service no longer occupies a port for itself and shares the port with the rest of the Application Server.

This immediately breaks the legacy code. Unfortunately, changing the port is not enough to put our old clients back on track. We have to change the communication protocol in the jndi.properties file, as follows (note the "http-remoting"):



java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
java.naming.provider.url=http-remoting://localhost:8080
jboss.naming.client.ejb.context=true
#username
java.naming.security.principal=joao
#password
java.naming.security.credentials=pedro


The only difference lies in the url line. A few other small differences may exist in what concerns the creation of users and the roles that users can play, but this is the detail where I lost most of the time.

Monday, April 15, 2013

Web Service with JAX-WS 2.2.4

(I have a post that supersedes this one here)

In this message I will create a very simple web service and respective client using the JAX-WS technology. I will deploy the web service on JBoss AS 7.1.1 and use Eclipse. NetBeans instead of Eclipse and other application server might do the trick equally well. First, let us start JBoss. Just cd to the bin directory and run it as follows:

./standalone.sh (standalone.bat if you are using Windows)

We must create a new dynamic web project:


Give it a name. Let us call it WSAgenda (you should not get any warning over the name repetition) and press the Finish button.



I will first create the PhoneBookServer class (File-> New -> and look for class).


Notice that we selected the package ws:
package ws;

import java.util.Hashtable;
import java.util.Map;

import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService
public class PhoneBookServer {
static private Map<String, String> phonebook = new Hashtable<>();
public PhoneBookServer() {
}
@WebMethod
public String getNumero(String nome) throws NoSuchPersonException {
if (!phonebook.containsKey(nome))
throw new NoSuchPersonException();
return phonebook.get(nome);
}
@WebMethod
public void setNumero(String nome, String numero) {
phonebook.put(nome, numero);
}
}



We will also create an exception class, for the case where the person looked for is not registered:

package ws;

public class NoSuchPersonException extends Exception {

private static final long serialVersionUID = 1L;

}

Note that we use a static hash table to keep the phone, because multiple objects of type PhoneBookServer may coexist for performance reasons. It would not make sense to have multiple server objects seeing different phone books and giving different replies to clients. One should also notice that the server has no numbers and that the client needs to add them.

To deploy the server, select the name of the project (WSPhoneBook on the left side) and do as follows with the right button of the mouse:


Then we set the name of the war file:


If things went well you should see the following trace in the JBoss window:


You should check the address in the 6th line, add "?wsdl" to it and put it in the browser:

http://localhost:8080/PhoneBook/PhoneBookServer?wsdl

You should see this:


The server is ready, let us write the client.

We first need to have stubs, to invoke the web services. But before we can do that we need to create a regular Java project. Let us call it WSClient. Now, find the directory where Eclipse writes the source files of this project. In my case it is long-path-to-workspace/WSClient/src/. Do

cd long-path-to-workspace/WSClient/src/

with the necessary differences in the Windows operating system. We must run the wsimport command with an option to keep java files and with a package that might be "artifact". Note that this command may not exist in your path. Two typical problems: 1) you installed a JRE instead of a JDK; 2) the bin directory of the JDK is not in your path. You may overcome problem 2 by indicating the complete path to the wsimport command.


We will now go to Eclipse to create Client and Client2 java classes, to write and read numbers to/from the phone book. Press F5 or use the right button to refresh. You should see your own classes and the stubs, as well.



Client should be like this:
import artifact.PhoneBookServer;
import artifact.PhoneBookServerService;


public class Client {

/**
* @param args
*/
public static void main(String[] args) {
PhoneBookServerService as = new PhoneBookServerService();
PhoneBookServer asp = as.getPhoneBookServerPort();
asp.setNumero("Paula", "234523452345");
asp.setNumero("Heloisa", "111111111111");
}

}


Run it. Client2 should be like this:

import artifact.NoSuchPersonException_Exception;
import artifact.PhoneBookServer;
import artifact.PhoneBookServerService;


public class Client2 {

/**
* @param args
* @throws NoSuchPersonException_Exception 
*/
public static void main(String[] args) throws NoSuchPersonException_Exception {
PhoneBookServerService as = new PhoneBookServerService();
PhoneBookServer asp = as.getPhoneBookServerPort();
System.out.println("Numero da Paula " + asp.getNumero("Paula"));
System.out.println("Numero da Heloisa " + asp.getNumero("Heloisa"));
System.out.println("Numero da Carla " + asp.getNumero("Carla"));
}

}



What a pity! The server does not really have the number of Carla, so we will get an exception when we try this program. So, these red letters are a god sign in our case. Everything went as expected:


Tuesday, December 4, 2012

Some hints about Assignment #3 - 2012/13

I'm afraid that web services are not working properly with version 4.11 server. Please try 4.10, but don't delete 4.11, as it has useful examples. The following are only suggestions, if you think of a simpler solution you are allowed (and encouraged) to use it:

1 - Unmarshall XML: use smooks.
2 - Splitter and Aggregator. You may write your own splitter. Check the streaming_aggregator example, the java class IncomingComposer (version 4.11). The difference as I explained in the class is that there is no final message to count the previous ones. Each message carries the total number of messages.
3 - Process details: check bpm_orchestrator4 (and the easier ones 1,2 and 3 before).
4 - Make the process reply: use <property name="reply-to-originator" value="true" /> in the action <action class="org.jboss.soa.esb.services.jbpm.actions.BpmProcessor" name="..."> of jboss-esb.xml. This requires that the process also uses <action class="org.jboss.soa.esb.services.jbpm.actionhandlers.EsbNotifier"> at some point (typically in the end). Again, this is done in the aforementioned examples (bpm_orchestrator4).
5 - Creation of web services and utilization as wrappers of the EJBs: in this blog message.
6 - Invoke web services: see example webservice_consumer1 (we may also see the others and use their approaches). You may also want to take a look at webservice_producer.

Don't forget to download the Programmers guide, from here.

Monday, November 19, 2012

The HelloWorld in the JBoss Enterprise Service Bus

INSTALLING JBOSS ESB

Let's start by istalling JBoss ESB. First the bad news: we can't use JBoss AS 7. We have two options, both of them with their own problems. Either we install (the full blown) JBoss ESB server version 4.11 or we need to revert to JBoss AS 6 and install the ESB (non-server) 4.11 on top of it.

(Option 1) It is easier to download and install the standalone server JBoss ESB distribution. However, it uses a pretty old JBoss distribution, the 4.2.3, at the time I write this document. This means that many of the code you have in Java EE may not run there.

(Option 2) If you go for the other option, JBoss AS 6 is available here. Pick the last one (6.1.0.Final). You can find the JBoss ESB server distributions here. Pick the one that is not server: jbossesb-4.11.zip. The documentation, which you can find here, explains how to install JBoss ESB into the JBoss AS 6 you downloaded. I found it simple and it the installation worked pretty well to me. Unfortunately, I never managed to run the bpm_orchestration3 example that comes with the code (the web interface crashes), something you will need to do in EAI. So, if this example do not work for you either, you should go for Option 1.

INSTALLING JBOSS DEVELOPER STUDIO

Next, we need to install the JBoss Developer Studio (JDS) IDE enriched with the SOA tooling. This may not be entirely trivial to do. For me it was hard to find a location from where to download the SOA tooling. Moreover, you must register on the RedHat site to download JDS.

At least in theory you may do similar steps to use plain Eclipse plus some plug-ins. Nevertheless, it is certainly easier to install JDS and SOA Tooling. Check these sites:
Jboss Developer Studio 5.x
SOA Tooling

This latter web page didn't work well for me. I had to download a zip to my local disk and use the Help->Install New Software menu of the JDS and point it to the location of the contents I downloaded to get the following image:


THE HELLOWORLD ESB PROJECT

Now, if everything went smoothly, we should be able to create an ESB project. Take a look at the jbossesb-server-4.11/samples directory and to the "helloworld" project. We will make a copy of it using JDS. Go to the File menu, pick "New" and look for ESB. You should get to this point:


You may want to set up the JBoss ESB for the project as well (by clicking next and next again):

The samples/helloworld example contains three java source files. Copy the MyJMSListenerAction.java to your JDS project.

You will also need the "jobss-esb.xml" file. Copy it into the "esbcontent/META-INF" folder. Don't be afraid to overwrite the file with the same name that was there before. Two more files:
deployment.xml, which contains a list of JMS queues required, and jam-queue-service.xml (Option 1 above) or hornetq-jms.xml (Option 2), which instructs JBOSS to create those queues. The "deployment.xml" goes to the "esbcontent/META-INF" directory, while the latter goes to the "esbcontent" directory. The organization of the files should look like this:

(Option 1)

or
(Option 2)

In Option 2, the file deployment.xml must change to look as follows, otherwise do not touch it:

<?xml version="1.0" encoding="UTF-8"?>
<jbossesb-deployment>
 <depends>org.hornetq:module=JMS,type=Queue,name="quickstart_helloworld_Request_esb"</depends>
 <depends>org.hornetq:module=JMS,type=Queue,name="quickstart_helloworld_Request_gw"</depends>
</jbossesb-deployment>


So, let's deploy the project, by taking the following steps:

Then:

 And finally (pick your own directory):

Check the URL http://localhost:8080/contract/ to see whether the service is running, as you can see in the lowest box of the following figure:

THE CLIENT PROJECT - USING JMS

Everything that follows was tried only for Option 1 above. For Option 2 slight changes may apply. Let us just use a standard Java Project. Go to File-->New and then you may either see the option to create a Java Project, or you may need to pick "Other":

You may call it the HelloWorldClient. To work on this project, you should change the perspective on the right upper corner to Java (this is not mandatory). Copy the two files SendJMSMessage.java and SendEsbMessage.java to the "src" folder of the project. Again, these files are in the samples/quickstarts/helloworld example. After you move everything to the appropriate package, you should still see library problems:

You need a lot of external jar files and a library to solve the JMS related problems. Include all the jar files in the following directories (this will be necessary for directly sending an ESB message):

$JBOSS_HOME/server/default/deploy/jboss-aop-jdk50.deployer
$JBOSS_HOME/server/default/lib/
$JBOSS_HOME/client/

I should say that at some point I had to make sure to include the jboss-messaging.jar before the jbossall-client.jar. But you should not need to do this.

To add the Library just press the "Add Library..." button, when you are managing the build path:


You may either find the following window or you may have to push the "Manage ESB Runtimes". In the latter case you should point to the home of your jboss, $JBOSS_HOME, to come back to this window with an option to select:

All project errors should go, once you finish this step. Now, let us start by firing the service via the JMS gateway queue. You need to set the parameters of SendJMSMessage, as follows:

Let us say "Hello":

And the result on the console of JBoss should be the following:



THE CLIENT PROJECT - USING ESB ASYNCHRONOUS DELIVERY

Now, let us invoke the ESB service directly, without resorting to JMS. This can be tricky, due to configuration problems. 

First, we need to create a folder name META-INF with the uddi.xml file inside. This file is in the samples/quickstarts/conf/registry/META-INF. You also need the jbossesb-properties.xml, which is in the helloworld directory. The tree should look as follows:


Check your source folders, to ensure that JDS did not exclude the META-INF folder. The next figure shows that no folder is excluded:



We will now run the SendEsbMessage class. Look at the arguments:



They match the service category and name that we defined in the server project, in the file jboss-esb.xml. You can also find them here: http://localhost:8080/contract/.


Once you run this, the JBoss console should display signs of your activity displaying a friendly "HelloESBWorld".