Suggested Pages

Saturday, September 29, 2012

Create and Install a Custom Maven Archetype - Tutorial

In this post I will show how to create a simple maven archetype.

Create Maven Archetype


You have to create a folder tree as this shown below:


STEP-1: Create a folder for the configuration files


As you can see in the previuos snapshot, you have to create a folder /META-INF/maven to put both archetype.xml and archetype-metadata.xml.

STEP-2: Create archetype.xml into folder /META-INF/maven/archetype.xml


In this file you should register all the resources of your archetype and distinguish among resources, sources, etc..
archetype.xml
<?xml version="1.0" encoding="UTF-8"?>
<archetype>
 <id>${artifactId}</id>
 <sources>
     <source>src/main/java/com/simonefolinoblogspot/mavenarchetype/main/Sample.java</source>
   </sources>
 <resources>
  <resource>src/main/webapp/resources/css/main.css</resource>
  <resource>src/main/webapp/resources/js/main.js</resource>
  <resource>src/main/webapp/WEB-INF/web.xml</resource>
  <resource>src/main/webapp/jsp/index.jsp</resource>
 </resources>
</archetype>
   

STEP-3: Create archetype-metadata.xml into folder /META-INF/maven/archetype-metadata.xml


archetype-metadata.xml

<?xml version="1.0" encoding="UTF-8"?>

<archetype-descriptor name="simple-webapp-archetype">
 <fileSets>
  <fileSet encoding="UTF-8" filtered="true">
   <directory>src/main/java</directory>
  </fileSet>
  <fileSet encoding="UTF-8" filtered="true">
   <directory>src/main/resources</directory>
  </fileSet>
  <fileSet encoding="UTF-8" filtered="true">
   <directory>src/main/webapp</directory>
  </fileSet>
 </fileSets>
</archetype-descriptor>


STEP-4: Create a pom.xml into the root of your maven archetype


In the following snippet of code you can notice that groupId, artifactId and versionId are parametrized.
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>${groupId}</groupId>
 <artifactId>${artifactId}</artifactId>
 <name>${artifactId}</name>
 <version>${version}</version>
 <packaging>jar</packaging>
 <build>
  <plugins>
   <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
     <encoding>UTF-8</encoding>
     <source>1.5</source>
     <target>1.5</target>
    </configuration>
   </plugin>
  </plugins>
 </build>
 <dependencies>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>servlet-api</artifactId>
   <version>2.4</version>
   <scope>provided</scope>
  </dependency>
  <dependency>
   <groupId>javax.servlet.jsp</groupId>
   <artifactId>jsp-api</artifactId>
   <version>2.0</version>
   <scope>provided</scope>
  </dependency>
 </dependencies>
</project>


STEP-5: Create the structure of your custom archetype into the folder <CUSTOM-MAVEN-ARCHETYPE>/src/main/resources/archetype-resources/


Under the folder archetype-resources you have to put the structure declared in the configuration files written in the previous steps. This folder is the container of your project's archetype.

Install Maven Archetype


Open the shell and go to the path <CUSTOM-MAVEN-ARCHETYPE> at the same level of the first pom.xml in the folder tree. Then you type the following instruction:

mvn clean 
    install 
        -DartifactId=archetypeArtifactId 
        -DgroupId=archetypeGroupId 
        -Dversion=1.0-snapshot 
    archetype:update-local-catalog 
    archetype:crawl


Generate project from Archetype


Open the shell and type the following instruction at the path you prefer.


mvn archetype:generate 
    -DinteractiveMode=false  
    -DarchetypeArtifactId=archetypeArtifactId   
    -DarchetypeGroupId=archetypeGroupId   
    -DarchetypeVersion=1.0-snapshot      
    -DgroupId=mygroup
    -DartifactId=myartifactId
    -Dversion=1.0-snapshot
    -DarchetypeRepository=/repository 
    -DarchetypeCatalog=local



Suggested Posts:


HTML Response and Content Types

HTTP Response and data stream


The HTTP protocol has two main phases of communication:
  • HTTP REQUEST;
  • HTTP RESPONSE.
The HTTP response is a single data stream, so when you ask for a web page containing images, embedded in the stream there are links to resources (as images) that need additional requests. The browser has the responsibility to make these requests.

Content-Type


An HTTP response has got many HTTP headers. One of these is called Content-Type. It specifies what type of file that has been sent by the server and it's used by the browser to process the data stream.
Here are the most common content types:
  • text/html: HTML;
  • application/octet-stream:Arbitrary binary data;
  • application/zip: ZIP archive files;
  • image/jpeg: Images files;


Is it possible to make an HTTP response with different content types?


If you want to send different files you should use a ZIP file.


Suggested Posts:

Encryption, Hashing and Session

You should not store passwords in clear in Session. Generally it's recommended to store hash password to avoid the reverse engineering.

Hashing


Registration


A common practice is to store hash password in databases and never store passwords as plain text anywhere in your application. During a registration , you provide a plain password that is used at this way:
  • A salt is added to to the plain password;
  • The new string (plain password salt) is used to generate an hash value with SHA-1 or MD5 algorithm;
  • The hash value is stored in DB.

Authentication


Authentication is a similar process of that described above. The plain password provided in the login formis used at this way:
  • A salt is added to to the plain password;
  • The new string ( plain password salt ) is used to generate an hash value with SHA-1 or MD5 algorithm;
  • The hash value is compared with the hash value stored in DB and retrieved by username.


Encryption

Encryption is different from hashing because you can decrypt the encrypted text to get the original text, instead hashing does not permit to obtain the original text from the hash value

Suggested Posts:

Friday, September 28, 2012

JPA Bidirectional Association

In this post we'll see an example of a bidirectional OneToMany-ManyToOne relationship in JPA.
A common example of OneToMany-ManyToOne relationship is Parent-Child relationship.

Object Point of View



Child.java

...

@Entity
public class Child {
 
 @Id
 private int childId;
 
 @Column
 private String name;
 
 @ManyToOne
 private Parent parent;

...

}

Parent.java

...
@Entity
public class Parent {
 
 @Id
 private int parentId;
 
 @Column
 private String name;

 @OneToMany(mappedBy="parent")
 private List children;
        
 ...
}


Database Point of View

create table Child (
        childId integer not null,
        name varchar(255),
        parent_parentId integer,
        primary key (childId)
)

create table Parent (
        parentId integer not null,
        name varchar(255),
        primary key (parentId)
)

alter table Child 
        add constraint FK3E104FC9E7EF479 
        foreign key (parent_parentId) 
        references Parent


As you can see, if you don't tell JPA to use a Join Table explicitly only two tables are created: the Child table that has a foreign key to Parent table .

JPA Set vs List

In this post we'll see the difference on the using of Set and List in a unidirectional association.

List and @JoinTable


Object Point of View

@Entity
public class Parent {
 
@Id
private int parentId;
 
@Column
private String name;

@OneToMany
@JoinTable(name="Parent_Child", joinColumns=@JoinColumn(name="parentId"), inverseJoinColumns=@JoinColumn(name="childId"))
private List<Child> children;

 ...

}


Database Point of View

create table Parent_Child (
        parentId integer not null,
        childId integer not null,
        unique (childId)
)

create table Child (
        childId integer not null auto_increment,
        name varchar(255),
        primary key (childId)
)

create table Parent (
        parentId integer not null auto_increment,
        name varchar(255),
        primary key (parentId)
)

alter table Parent_Child 
        add index FK5AF11D47C916AC84 (parentId), 
        add constraint FK5AF11D47C916AC84 
        foreign key (parentId) 
        references Parent (parentId)

alter table Parent_Child 
        add index FK5AF11D47ABE0291E (childId), 
        add constraint FK5AF11D47ABE0291E 
        foreign key (childId) 
        references Child (childId)



Set and @JoinTable


Object Point of View


@Entity
public class Parent {

@Id
private int parentId;

@Column
private String name;

@OneToMany
@JoinTable(name="Parent_Child", joinColumns=@JoinColumn(name="parentId"), inverseJoinColumns=@JoinColumn(name="childId"))
private Set<Child> children;


...
}


Database Point of View

create table Parent_Child (
        parentId integer not null,
        childId integer not null,
        primary key (parentId, childId),
        unique (childId)
)


create table Child (
        childId integer not null auto_increment,
        name varchar(255),
        primary key (childId)
)


create table Parent (
        parentId integer not null auto_increment,
        name varchar(255),
        primary key (parentId)
)


alter table Parent_Child 
        add index FK5AF11D47C916AC84 (parentId), 
        add constraint FK5AF11D47C916AC84 
        foreign key (parentId) 
        references Parent (parentId)

alter table Parent_Child 
        add index FK5AF11D47ABE0291E (childId), 
        add constraint FK5AF11D47ABE0291E 
        foreign key (childId) 
        references Child (childId)


As you can see the join table Parent_Child has both a primary key both a unique constraint.

So the most important difference is that the conversion into database scripts, preserves the concept of uniqueness of Child entity into the collection associated to a specific Parent.

You cannot associate the same Child object to the same Parent object, as you cannot insert into database two entries with the same couple (Child,Parent) into Parent_Child table.

Thursday, September 27, 2012

Forward, Include and Redirect

In this post we'll see the differences among the techniques of forward, include and redirect .

Forward


The method forward of RequestDispatcher, allows the server to literally forward the request to another resource (same server or same domain) maintaining all request's parameters. It's usually used when you want the request to be served by another resource (Servlet or JSP) in place of the calling Servlet.

Include


The method include of RequestDispatcher, allows the server to include the contents of the specified resource in the flow of the servlet response. The included resource cannot change:
  • The response status code;
  • The HTTP headers.
Any attempt to modify headers is ignored.

Redirect


The method sendRedirect of HttpServletResponse, tells to the client that it has to send a new request to the pointed path. Therefore it causes two requests to the server from the client. It's used generally to redirect towards other servers and domains.

Example


In the following snippets of code, you have a complete example of how forward, include and redirect work. You can notice that:
  • If you click the anchor Forward, HiServlet forwards to hi.jsp and the printed output is:
    hi=good morning method=forward

  • If you click the anchor Incluce, HiServlet includes hi.jsp and the printed output is:
    hi=good morning method=forward

  • If you click the anchor Redirect, HiServlet tells the client to do another request to the URL http://<host>/hiServlet/jsp/hi.jsp. The printed output is:
    hi=null method=null

  • If you click the anchor Forward & ChangeHeader, HiServlet forwards to hi.jsp and the header Content-Type is set to application/octet-stream:

  • If you click the anchor Include & ChangeHeader, HiServlet includes hi.jsp and the header Content-Type is not set to application/octet-stream

  • If you click the anchor Redirect & ChangeHeader, HiServlet redirects to hi.jsp and the header Content-Type is not set to application/octet-stream
web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 <display-name>hiServlet</display-name>
 
 <servlet>
  <servlet-name>hiServlet</servlet-name>
  <servlet-class>com.simonefolinoblogspot.servlet.HiServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet-mapping>
  <servlet-name>hiServlet</servlet-name>
  <url-pattern>*.do</url-pattern>
 </servlet-mapping>
 
 <welcome-file-list>
  <welcome-file>/jsp/index.jsp</welcome-file>
 </welcome-file-list>

</web-app>

HiServlet

...

public class HiServlet extends HttpServlet {

 private static final long serialVersionUID = 6167525405783177839L;

 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
   throws ServletException, IOException {
  
  System.out.println("doGet()--start");
  
  String method = req.getParameter("method"); 
 
  System.out.println("method="+method);
  
  req.setAttribute("hi", "good morning");
  
        if(method.equals("forward")){
         RequestDispatcher requestDispatcher = req.getRequestDispatcher("/jsp/hi.jsp"); 
         requestDispatcher.forward(req, resp);
        
        }else if(method.equals("include")){
         RequestDispatcher requestDispatcher = req.getRequestDispatcher("/jsp/hi.jsp");
         requestDispatcher.include(req, resp);
         
        }
        else if (method.equals("redirect")){
         resp.sendRedirect("/hiServlet/jsp/hi.jsp");         
        }
        else{
         resp.sendError(500);
        }
        System.out.println(req.getHeader("Content-Type"));
        System.out.println("doGet()--end");
 }

}

index.jsp
<html>
<head>
<title>EncodePageExample</title>
</head>

<body>
 <ul>
  <li><a href="/hiServlet/hi.do?method=forward">Forward</a></li>
  <li><a href="/hiServlet/hi.do?method=include">Include</a></li>
  <li><a href="/hiServlet/hi.do?method=redirect">Redirect</a></li>
  
  <li><a href="/hiServlet/hi.do?method=forward&changeContentType=application/octet-stream">Forward & ChangeHeader</a></li>
  <li><a href="/hiServlet/hi.do?method=include&changeContentType=application/octet-stream">Include & ChangeHeader</a></li>
  <li><a href="/hiServlet/hi.do?method=redirect&changeContentType=application/octet-stream">Redirect & ChangeHeader</a></li>
  
  
 </ul>
</body>

</html>
hi.jsp

<html>
<head>
 <title>DispatcherExample</title>
</head>

<body>

 <%
  String hi=(String)request.getAttribute("hi");
  String method=(String)request.getParameter("method");
  out.println("hi="+hi);
  out.println("method="+method);
  String changeContentType=(String) request.getParameter("changeContentType");
  if(changeContentType!=null){
    response.setHeader("Content-Type", changeContentType);
  }
 %>
 
</body>

</html>

Monday, September 24, 2012

SessionCookie vs URL Rewriting

In this post I will write about HTTP Session in a Web Application. There are a few ways to design session:
  • Store session ID in cookie file
  • Store session ID in the URL

Session Cookie


HTTP Session can be considered as a server area to store information that have to be shared over multiple HTTP requests.
The server uses the session identifier sent by the client to identify the session which the client belongs to. The server reads that ID in order to refer to the session data used by that client.
Generally the session identifier is on the client browser and it's sent to the server, but when the browser windows are closed, the browser deletes the session identifier.
Session IDs are usually stored in a cookie called Session cookie
Session ID is assigned when a new Session is created. Generally this happened:
  • When it's the first time access to the server;
  • When the browser is closed and the cookie is deleted;
  • When the server terminates a session after a few minutes of inactivity.



URL Rewriting

URL Rewriting is a mechanism that allows the server to associate an HTTP request to a stored session on the server when a client disables cookie storing.
Server uses the ID send with the URL to track sessions. For example an URL like this:
   <a href="/add/link">ADD LINK</a>
is rewritten into
    <a href="/add/link;jsessionid=DA22145SSGE2">ADD LINK</a>
The web server extracts jsessionid from the URL to obtain the reference to the HttpSession. To produce this translaction you can use encodeURL() and encodeRedirectURL()

Let's suppose to have a servlet EncodeUrlServlet.
web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 <display-name>encodeurl</display-name>
 
 <servlet>
  <servlet-name>encodeUrlServlet</servlet-name>
  <servlet-class>com.simonefolinoblogspot.servlet.EncodeUrlServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet-mapping>
  <servlet-name>encodeUrlServlet</servlet-name>
  <url-pattern>*.do</url-pattern>
 </servlet-mapping>
 
</web-app>

EncodeUrlServlet.java

package com.simonefolinoblogspot.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class EncodeUrlServlet extends HttpServlet{

 private static final long serialVersionUID = 6167525405783177839L;

 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
   throws ServletException, IOException {
  
  
  // if no session is found, a new one is created
  HttpSession session = req.getSession(true);
  
  System.out.println("session id="+session.getId());
  
  String actionURL="/session-encodeurl/encode.do";
  
  req.setAttribute("actionURL",resp.encodeURL(actionURL));
  
  req.getRequestDispatcher("/jsp/encode.jsp").forward(req, resp);
  
 }
  
}

encode.jsp

<html>
<head>
 <title>EncodePageExample</title>
</head>

<body>

 <a href="<%=request.getAttribute("actionURL")%>">Call Again </a>
 
</body>

</html>

If you disable the cookies on your browser and ask for http://<hostname>/<context>/encode.do you are forwarded to encode.jsp that prints the HTML written below.
encode.jsp

<html>
<head>
 <title>EncodePageExample</title>
</head>

<body>

 <a href="/session-encodeurl/encode.do;jsessionid=820F618F79E21B5E541CE129C0E0EFE5">Call Again </a>
 
</body>

</html>

Tuesday, September 11, 2012

Hibernate Session vs SessionFactory

In Hibernate there are two important classes to know:

Session is not thread safe


Session it is not implemented to be thread safe.
In a multi-thread environment each thread/transaction should obtain its own instance of Session from a SessionFactory.
See Session API.

SessionFactory is thread safe


SessionFactory is immutable. Once the session has created its state it's set for all the life time.
See Session Factory API.

Immutable Objects


For more information about Immutable Object see Immutable Object

Cannot create TypedQuery for query with more than one return - Problem

In this post it's shown a common problem when using a typed query in JPA.

Problem


NamedQuery


@NamedQuery(name = "User.findUserByUsername", query = "SELECT u,p FROM User u JOIN u.profile p WHERE u.username=:username"),


Stacktrace

java.lang.IllegalArgumentException: Cannot create TypedQuery for query with more than one return
 at org.hibernate.ejb.AbstractEntityManagerImpl.createNamedQuery(AbstractEntityManagerImpl.java:506)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)



Solution


Replace this:
 
   entityManager.createNamedQuery("User.findUserByUsername",User.class);

with

  entityManager.createNamedQuery("User.findUserByUsername");

Web Service with Apache CXF - Tutorial


In this post we'll see an example of a Web Service with Apache CXF using Maven.

Step 1: Declare Maven dependencies in your pom.xml


pom.xml

 <!-- apache cxf  --> 
 <dependency>
     <groupId>org.apache.cxf</groupId>
     <artifactId>cxf-rt-frontend-jaxws</artifactId>
     <version>${cxf.version}</version>
 </dependency>
 <dependency>
     <groupId>org.apache.cxf</groupId>
     <artifactId>cxf-rt-transports-http</artifactId>
      <version>${cxf.version}</version>
 </dependency>

 <!-- spring --> 
 <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>${spring.version}</version>
 </dependency>
 <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context-support</artifactId>
     <version>${spring.version}</version>
 </dependency>
 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>${spring.version}</version>
 </dependency>


Step 2: Add CXFServlet into the web.xml of your Web application



<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 <display-name>apachecxfwsexample1</display-name>


 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>

 <!-- middleware configuration -->
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath*:META-INF/spring/spring-*.xml</param-value>
 </context-param>

 <!-- Apache CXF Servlet -->
 <servlet>
     <servlet-name>CXFServlet</servlet-name>
     <servlet-class>
         org.apache.cxf.transport.servlet.CXFServlet
     </servlet-class>
   </servlet>
 
 <servlet-mapping>
      <servlet-name>CXFServlet</servlet-name>
      <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>


Step 3: Create the Web Service


First write the SEI (Service Endpoint Interface) HelloService annotated with @WebService, then the class HelloServiceImpl that implements the interface.

HelloService.java
package com.simonefolinoblogspot.apachecxf.ws;

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

/**
 * 
 * 
 * Service Endpoint Interface (SEI)
 *
 */
@WebService
public interface HelloService {
 
 @WebMethod(operationName="sayHello")
 public String sayHello(@WebParam(name="name")String name);

}


HelloServiceImpl.java

package com.simonefolinoblogspot.apachecxf.ws;

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


@WebService(endpointInterface="com.simonefolinoblogspot.apachecxf.ws.HelloService",serviceName = "HelloService")
public class HelloServiceImpl implements HelloService{

 
 @WebMethod(operationName="sayHello")
 public String sayHello(@WebParam(name="name")String name) {
  return "hi "+name+" !";
 }

}

Step 4: Create the configuration file cxf-servlet.xml


In the file web.xml it's declared cxf-servlet.xml as the file to put the configurations of web services.
cxf-servlet.xml


<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://cxf.apache.org/jaxws
 http://cxf.apache.org/schemas/jaxws.xsd">
 
 <!-- START needed to import apache cxf configuration START -->
 <import resource="classpath:META-INF/cxf/cxf.xml" />
 <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
 <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
 <!-- END needed to import apache cxf configuration  END-->

 <!-- the service is available at http://<hostname>/<context>/helloservice -->

 <jaxws:endpoint id="helloservice"
  implementor="com.simonefolinoblogspot.apachecxf.ws.HelloServiceImpl"
  address="/helloservice" />
</beans>


Step 5: Write a Client to test the web service




package com.simonefolinoblogspot.apachecxf.ws.client;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;

import com.simonefolinoblogspot.apachecxf.ws.HelloService;

public final class Client {

    private static final QName SERVICE_NAME  = new QName("http://ws.apachecxf.simonefolinoblogspot.com/", "HelloService");
    private static final QName PORT_NAME = new QName("http://ws.apachecxf.simonefolinoblogspot.com/", "HelloServicePort");


    private Client() {
    } 

    public static void main(String args[]) throws Exception {
        Service service = Service.create(SERVICE_NAME);
        // Endpoint Address
        String endpointAddress = "http://localhost:8083/apachecxfwsexample1/helloservice";

        // Add a port to the Service
        service.addPort(PORT_NAME, SOAPBinding.SOAP11HTTP_BINDING, endpointAddress);
        
        HelloService hw = service.getPort(HelloService.class);
        System.out.println(hw.sayHello("Simone"));

    }

}

Sunday, September 2, 2012

Stateful Session Bean Clustered - Tutorial

In this post I'll show an example of a Stateful Sesson Bean Clustered that implements a simple counter.
In a few steps we'll see how to configure a load balancer with Apache HTTP Server using Session Affinity on two JBoss nodes. Then we'll see the behaviour of the implemented Session Bean after a simulated failover: when one of the JBoss nodes fails, the Session will be replicated on the other active JBoss node.

Step-1: Configure Apache HTTP Server


First we have to configure the files <APACHE-HOME>/conf/workers.properties and <APACHE-HOME>/conf/httpd-vhosts.conf.
conf/workers.properties

=\
# Define list of workers that will be used
# for mapping requests
worker.list=loadbalancer,status

# Define Node1
# modify the host as your host IP or DNS name.
worker.node1.port=8009
worker.node1.host=node1.counter.cluster.com
worker.node1.type=ajp13
worker.node1.lbfactor=1
worker.node1.cachesize=10

# Define Node2
# modify the host as your host IP or DNS name.
worker.node2.port=8009
worker.node2.host= node2.counter.cluster.com
worker.node2.type=ajp13
worker.node2.lbfactor=1
worker.node2.cachesize=10

# Load-balancing behaviour
worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=node1,node2
worker.loadbalancer.sticky_session=1
#worker.list=loadbalancer

# Status worker for managing load balancer
worker.status.type=status
httpd-vhosts.conf


<VirtualHost *:80>
  DocumentRoot “/local/docroot/htdocs”
  Include conf/tuo.conf
  JkMount /cluster/* loadbalancer
  ...
</VirtualHost>



Step-2: Startup of the JBoss nodes


We have to startup the two JBoss instances that are on the same subnet (to make it simple).

Startup of JBoss node on address 10.3.0.110


./run.sh -c  all  -b  10.3.0.110 -Djboss.messaging.ServerPeerID=1


Startup of JBoss node on address 10.3.0.109


./run.sh -c  all  -b  10.3.0.109 -Djboss.messaging.ServerPeerID=2


Wait the two nodes start communicating each other


11:52:10,764 INFO  [DefaultPartition] New cluster view for partition DefaultPartition (id: 1, delta: 1) : [10.3.0.110:1099, 10.3.0.109:1099]
11:52:10,766 INFO  [DefaultPartition] I am (10.3.0.110:1099) received membershipChanged event:
11:52:10,767 INFO  [DefaultPartition] Dead members: 0 ([])
11:52:10,767 INFO  [DefaultPartition] New Members : 1 ([10.3.0.109:1099])
11:52:10,767 INFO  [DefaultPartition] All Members : 2 ([10.3.0.110:1099, 10.3.0.109:1099])
11:52:10,890 INFO  [RPCManagerImpl] Received new cluster view: [10.3.0.110:53063|1] [10.3.0.110:53063, 10.3.0.109:1102]
11:52:22,027 INFO  [GroupMember] org.jboss.messaging.core.impl.postoffice.GroupMember$ControlMembershipListener@4d16c81d got new view [10.3.0.110:53063|1] [10.3.0.110:53063, 10.3.0.109:1102], old view is [10.3.0.110:53063|0] [10.3.0.110:53063]
11:52:22,028 INFO  [GroupMember] I am (10.3.0.110:53063)
11:52:22,028 INFO  [GroupMember] New Members : 1 ([10.3.0.109:1102])
11:52:22,028 INFO  [GroupMember] All Members : 2 ([10.3.0.110:53063, 10.3.0.109:1102])

As you can see the two instances has found each other and communicates with JGroup protocol. Afterwards this communication the cluster consists of two instances: Received new cluster view: [10.3.0.110:53063|1] [10.3.0.110:53063,10.3.0.109:1102].

Step-3: Write the Statefull Session Bean Clustered


We have to write a simple Session Stateful Bean that uses an instance variable called count. This counter is incremented on each call to the enterprise method.

CounterServiceRemote.java
...

import javax.ejb.Remote;

@Remote
public interface CounterServiceRemote {
 public void increase();
}


CounterService.java
....

import javax.ejb.Stateful;

import org.jboss.ejb3.annotation.Clustered;

/**
 * Session Bean implementation class CounterService
 */
@Stateful
@Clustered
public class CounterService implements CounterServiceRemote{

    private int count=0;
    
    public CounterService() {
       
    }
    public void increase(){
     System.out.println("counter:"+count);
     count++;
     System.out.println("counter:"+count);
    }

}



Step-4: Deploy the jar containing the EJB in both the JBoss nodes


You have to put the jar into the directory <JBOSS-NODE>/server/all/deploy.

Step-5: Wait the deploy to be completed


Console Output

12:33:50,115 INFO  [JndiSessionRegistrarBase] Binding the following Entries in G
lobal JNDI:

        CounterService/remote - EJB3.x Default Remote Business Interface
        CounterService/remote-com.clusterproject.CounterServiceRemote - EJB3.x R
emote Business Interface

12:33:50,205 INFO  [PlatformMBeanServerRegistration] JBossCache MBeans were succ
essfully registered to the platform mbean server.
12:33:50,225 INFO  [STDOUT]


Step-6: Verify in the JMX console that the Stateful Session Bean is registered



Step-7: Verify in the JMX console that the cluster is online



Step-8: Run the EJB client


ClientEJB.java

....

import javax.naming.NamingException;
import com.clusterproject.CounterServiceRemote;

public class ClientEJB {

   public static void main(String[] args) {
 try {
  Object obj =           
ServiceLocator.getInitialContext().lookup("CounterService/remote");
  CounterServiceRemote counterServiceRemote =(CounterServiceRemote) obj;
  counterServiceRemote.incrementa();
  Thread.sleep(10000);
  counterServiceRemote.incrementa();
  Thread.sleep(10000);
  counterServiceRemote.incrementa();
 } catch (NamingException e) {
  e.printStackTrace();
 } catch (InterruptedException e) {
  e.printStackTrace();
 }
  } 
}


ServiceLocator.java
....

import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class ServiceLocator {

 public static Context getInitialContext(){
  Properties properties =new Properties();
  properties.put(Context.INITIAL_CONTEXT_FACTORY,"org.jnp.interfaces.NamingContextFactory");
  properties.put(Context.PROVIDER_URL, "jnp://counter.cluster.com:1099");
  InitialContext jndiContext=null;
  try {
   jndiContext=new InitialContext(properties);
  } catch (NamingException e) {
   e.printStackTrace();
  }
  return jndiContext;
 }
}

It's important to notice that:
  • The instruction Thread.sleep(10000) is useful to wait the simulated fault of JBoss node;
  • The address counter.cluster.com is the jnp address of the load balancer machine. The EJB client has to know the address of the balancer machine.
After running the EJB client the below message will be written by the JBoss node that will serve the request.

INFO  [STDOUT] counter:0
INFO  [STDOUT] counter:1



Step-9:Shutdown of the serving JBoss node


During the waiting caused by Thread.sleep(10000), we have to shutdown the serving JBoss node. In this example I have used the JBoss instance on 10.3.0.110.

After the shutdown completes, the active JBoss node will continue serving the request. As you can see in the following console lines:
  • Dead members: 1 ([10.3.0.110:35429])
  • All Members : 1 ([10.3.0.109:1148])

Output

12:38:36,377 INFO  [RPCManagerImpl] Received new cluster view: [10.3.0.110:35429 |6] [10.3.0.109:1148]
12:38:36,577 INFO  [GroupMember] org.jboss.messaging.core.impl.postoffice.GroupM
ember$ControlMembershipListener@191a87d got new view [10.3.0.109:1148|10] [10.3.0.109:1148], old view is [10.3.0.109:1148|9] [10.3.0.109:1148, 10.3.0.110:35429]
12:38:36,577 INFO  [GroupMember] I am (10.3.0.109:1148)
12:38:36,587 INFO  [GroupMember] Dead members: 1 ([10.3.0.110:35429])
12:38:36,587 INFO  [GroupMember] All Members : 1 ([10.3.0.109:1148])
12:38:37,779 INFO  [RPCManagerImpl] Received new cluster view: [10.3.0.109:1148|
10] [10.3.0.109:1148]
12:38:37,849 INFO  [DefaultPartition] New cluster view for partition DefaultPart
ition (id: 10, delta: -1) : [10.3.0.109:1099]
12:38:37,849 INFO  [DefaultPartition] I am (10.3.0.109:1099) received membership
Changed event:
12:38:37,849 INFO  [DefaultPartition] Dead members: 1 ([10.3.0.110:1099])
12:38:37,849 INFO  [DefaultPartition] New Members : 0 ([])
12:38:37,859 INFO  [DefaultPartition] All Members : 1 ([10.3.0.109:1099])
12:38:39,211 INFO  [STDOUT] counter:1
12:38:39,211 INFO  [STDOUT] counter:2
12:38:49,245 INFO  [STDOUT] counter:2
12:38:49,245 INFO  [STDOUT] counter:3



In summary this is only a simple rough example of a Clustered Session Bean, but I hope this post has arouse your curiosity about this subject.

Suggested Pages