Monday, October 27, 2014

An Enterprise Application Repository to Access the Players Table

In my previous message, I explained how you could create a stand-alone JPA project using EclipseLink. Now, I'm going to pick this project and turn it into a part of a more complete Enterprise Application Repository. Overall, we are going to need four different projects, all of them interconnected:

  1. Java Persistence API
  2. Enterprise JavaBeans
  3. Dynamic Web Project
  4. Enterprise Application Repository
This is only a small tutorial with minimal implementation. Production code would require a better decoupling of layers (I will mention this below).

The Java Persistence API project

Let us start by the JPA project, which is almost the same as we had in the EclipseLink example. First you need to do File--> New--> JPA Project, to reach the following window:


After a couple of Next's you reach the following window, where you tell Eclipse that the JPA library (Hibernate) will be provided by the Runtime, i.e., WildFly:


Once we press the Finish button we may start copying (sorry for this redundancy) the Player and Team code:

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

And now the Team:



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

}


I will not go into details on this code in this message, because I gave some in the previous one. You may simply copy the files from one project to the other, if you red the previous example. The one thing that is different is the persistence.xml file. It looks like this:

<?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" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/PlayersEAR</jta-data-source>
<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="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.hbm2ddl.auto" value="create" />
</properties>

</persistence-unit>
</persistence>


Notice the reference to the database in the javax.persistence.jdbc.url. You may use exactly the same database as in the previous example. It will be useful to fill data for us here. We are going to use the Java Transactions API (TPA) and we need to refer to the jta-data-source. The name we put here (java:/PlayersEAR) must exist in a configuration file of WildFly. It could be standalone.xml, or standalone-full.xml if you need Java Message Service. Look for the tag <datasources> to insert this piece of configuration:


                <datasource jta="true" jndi-name="java:/PlayersEAR" pool-name="my_pool2" enabled="true" use-java-context="true" use-ccm="true">
                    <connection-url>jdbc:mysql://localhost:3306/PlayersAndTeams</connection-url>
                    <driver>mysql</driver>
                    <security>
                        <user-name>artur</user-name>
                        <password>***</password>
                    </security>
                    <statement>
                        <prepared-statement-cache-size>100</prepared-statement-cache-size>
                        <share-prepared-statements>true</share-prepared-statements>
                    </statement>

                </datasource>


In fact, configuration of WildFly is more complicated than this. Look for the following messages on how to make WildFly work with MySQL:


I have this configured on WildFly, but I haven't read the specific messages on WildFly:


The EJB Project

Our next step is to create the Enterprise JavaBeans project. We will need a new specific "EJB Project" for that:



We need to add a reference to the previous ServerPlayers-JPA to refer the Player class in this project (see the source code, after this picture):



We will write a very simple Enterprise JavaBean (EJB). It contains two methods: one to populate the database, the other to search for players taller than a given value. This class refers an EJB remote interface, which we present afterwards. To create the bean and the respective interface, you need to do File-->New-->Session Bean, as in the figure.



The EJB implementation. You may want to take a longer more detailed look at the query:


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


Here are the two methods that are visible from the outside:


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




The Dynamic Web Project

Now, we will provide a web access to the EJB, through a servlet. For this we must first create a Dynamic Web Project (via File-->New):



Again, we need to refer to the other two projects (more on this below):


 It would be better to use Data Transfer Objects between the EJB and the presentation layer, to ensure a strict separation between the presentation layer and the data layer, as they are not adjacent. A library like MapStruct may help here. This would make the example more complicated. Maybe in the future I will ensure this strict separation.

We now create the servlet (File-->New-->Servlet):



The source code next:


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

}


I must say that this is not the most elegant code I've ever written, as I control the operation I want using two URLs and I have an HTTP GET command changing the state of the system (it should be a POST). Anyway, the get accepts two commands:

http://localhost:8080/ServerPlayers-Web/PlayersTallerThan?height=1.80, where 1.80 might be changed to the "height" threshold you want to set. But before we are able to do this, we must populate the database with:

http://localhost:8080/ServerPlayers-Web/PlayersTallerThan?fill=1

The parameter "fill" can actually have any value, the code will still work.

The Enterprise Application Repository (EAR) Project

Before we can put anything to run, we must create and deploy an EAR project. We start with File-->New--> Enterprise Application Project:


Once you push "Next" you are prompted to select the projects that compose the EAR. In our case we have the following three:


Once this step is finished, we may deploy the whole package, as follows (use the right button of the mouse):

Select the appropriate location in the deployments directory of the application server:

I assume that WildFly was running. Once you do the deployment, you should see the following output in WildFly, with the references to our project:


We can now go to a browser and enter the following URLs:

http://localhost:8080/ServerPlayers-Web/PlayersTallerThan?fill=1

to fill the database, and

http://localhost:8080/ServerPlayers-Web/PlayersTallerThan?height=1.80

should produce the following outcome:



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.