Pages

Friday, December 14, 2012

Exposing EJB as Web Service

Web Services are now industry standard for application integration, especially if you have a wider audience to support. Web Services provide seamless interoperability across heterogeneous applications in a loosely coupled way. More and more organizations are now moving towards a Service Oriented Architecture (SOA) where the functionality is provided as a Service. Details of the Web Services Architecture are out of the scope of this tutorial, however, you can go to this W3C link for more details.

JCP identified this trend and focused on providing out of the box support for Web Services in the Java EE from ground up. As a result of their efforts, creating and consuming the SOAP services is a breeze in java. However, the inherent communication architecture of SOAP means that you will be sending a significant amount of XML data working with SOAP services. For that reason RMI may be a better option for you unless you are looking to expose your services to non-java clients.

In this tutorial I will be presenting how you can expose an EJB as a web service and invoke that service with a simple client. All you need to do to expose the EJB as a web service is to annotate the EJB with the @WebService annotation and annotate all the methods that you want to expose as a service with @WebMethod annotation. Refer to my older post for a tutorial on how to create and deploy an EJB project in Glassfish for the full project. Here is a sample EJB that is part of the project I created earlier that is exposed as a web service.



package com.test.webservice.soap;

import javax.ejb.Stateless;
import javax.jws.WebMethod;
import javax.jws.WebService;

@Stateless
@WebService(targetNamespace="http://www.mynamespace.co.uk")
public class TestSOAP {

 @WebMethod
 public String sayHello(String name) {
  String str = "SOAP Says Hello " + name;
  return str;
 }
}

All we have done here is annotate the class as a @WebService and annotate the method as @WebMethod and Glassfish will do the rest for us. Refer to my other tutorial that I created earlier explaining how to create and deploy an EJB. All you have to do is to add another EJB to that same application and annotate that EJB with these Web Service annotations and deploy that to the Glassfish or any other EJB Application Server and the server will do the rest for you.

Once the EJB is deployed you can go to the Glassfish admin console to double check if your service is listed under the Web Services section of Glassfish menu. You can also double check your service by going to the URL http://localhost:8080/TestSOAPService/TestSOAP?tester which is created as a test page for your service. Notice this URL closely, the First part is where your web application is listening for any HTTP requests and then "TestSOAPService" is actually the ClassName + Service and then ClassName with a ?tester . This is the standard pattern whenever you will create and deploy your services, Glassfish will create a test page for you in the same format. This test page will invoke the service and will show you the response.

If you click on the WSDL File link, it will show you the WSDL file for that service.


<?xml version="1.0" encoding="UTF-8"?>
 <!--
  Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is
  JAX-WS RI 2.1.3.3-hudson-757-SNAPSHOT.
 -->
 <!--
  Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is
  JAX-WS RI 2.1.3.3-hudson-757-SNAPSHOT.
 -->
<definitions
 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.mynamespace.co.uk"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/"
 targetNamespace="http://www.mynamespace.co.uk" name="TestSOAPService">
 <ns1:Policy xmlns:ns1="http://www.w3.org/ns/ws-policy"
  wsu:Id="TestSOAPPortBinding_sayHello_WSAT_Policy">
  <ns1:ExactlyOne>
   <ns1:All>
    <ns2:ATAlwaysCapability xmlns:ns2="http://schemas.xmlsoap.org/ws/2004/10/wsat"></ns2:ATAlwaysCapability>
    <ns3:ATAssertion xmlns:ns4="http://schemas.xmlsoap.org/ws/2002/12/policy"
     xmlns:ns3="http://schemas.xmlsoap.org/ws/2004/10/wsat"
     ns1:Optional="true" ns4:Optional="true"></ns3:ATAssertion>
   </ns1:All>
  </ns1:ExactlyOne>
 </ns1:Policy>
 <types>
  <xsd:schema>
   <xsd:import namespace="http://www.mynamespace.co.uk"
    schemaLocation="http://localhost:8080/TestSOAPService/TestSOAP?xsd=1"></xsd:import>
  </xsd:schema>
 </types>
 <message name="sayHello">
  <part name="parameters" element="tns:sayHello"></part>
 </message>
 <message name="sayHelloResponse">
  <part name="parameters" element="tns:sayHelloResponse"></part>
 </message>
 <portType name="TestSOAP">
  <operation name="sayHello">
   <input message="tns:sayHello"></input>
   <output message="tns:sayHelloResponse"></output>
  </operation>
 </portType>
 <binding name="TestSOAPPortBinding" type="tns:TestSOAP">
  <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
   style="document"></soap:binding>
  <operation name="sayHello">
   <ns5:PolicyReference xmlns:ns5="http://www.w3.org/ns/ws-policy"
    URI="#TestSOAPPortBinding_sayHello_WSAT_Policy"></ns5:PolicyReference>
   <soap:operation soapAction=""></soap:operation>
   <input>
    <soap:body use="literal"></soap:body>
   </input>
   <output>
    <soap:body use="literal"></soap:body>
   </output>
  </operation>
 </binding>
 <service name="TestSOAPService">
  <port name="TestSOAPPort" binding="tns:TestSOAPPortBinding">
   <soap:address location="http://localhost:8080/TestSOAPService/TestSOAP"></soap:address>
  </port>
 </service>
</definitions>

WSDL is the definition file for that service. When publish your service to others, they will need this file to communicate with your service. We will also need the WSDL when we will create the Web Service client later on. That’s pretty much it. We are now ready to accept SOAP requests from any clients and serve them with our brand new Hello Service.

Client:

Now let’s create a client. Java based clients use the JAX-RPC API to access the web service. All we need now is the WSDL file. The easiest way to do that is to use the wsimport command that will generate all the necessary files you need to communicate with the web service.

Either download the file or directly point to the WSDL URI in your command. You can find the WSDL at http://localhost:8080/TestSOAPService/TestSOAP?wsdl. Here is the output of wsimport command:


C:\>wsimport -keep -p com.test.soap.generated -d C:\software\test\TestServiceClient\src\ http://localhost:8080/TestSOAPService/TestSOAP?wsdl
parsing WSDL...
generating code...
compiling code...
C:\>

Note here that the directory structure (com.test.soap.generated) you specified in the wsimport command must already exist otherwise it will throw errors. Also the directory you specify where you want to put all of the generated files.

Here is a sample java class that is using these generated files to communicate with the Web Service.



package com.test.soap.client;

import com.test.soap.generated.TestSOAP;
import com.test.soap.generated.TestSOAPService;

public class WebServiceClient {

 public static void main(String[] args) {
  
  System.out.println("Creating Service Handler...");
  TestSOAPService ss = new TestSOAPService();
  
  System.out.println("Acquiring Service Port...");
  TestSOAP port = ss.getTestSOAPPort();
  
  String msg=null;
  try{
   System.out.println("Invoking Service...");
   msg = port.sayHello("Raza");
  }catch(Exception e){
   msg = "Failed to open SOAP Request"; 
   e.printStackTrace();
  }
  
  System.out.println(msg);

 }

}

And here is the output when you run this class



Creating Service Handler...
Acquiring Service Port...
Invoking Service...
SOAP Says Hello Raza

I guess this will give you a good head start with the Web Services architecture as implemented by Java. You can now take this as a skeleton to build your applications around and play with more sophisticated options available.

Thursday, December 13, 2012

JSF - EJB 3.0 on Glassfish V2

This is a very basic tutorial on how to create and deploy a very basic JSF - EJB application on Glassfish application server. The whole process is very simple and straight forward. I will be using Glassfish V2 as the Application Server and Eclipse IDE to develop the application.

Before we begin, we need to setup the environment. For this very basic tutorial, all we need is a Glassfish AS installed and an Eclipse IDE downloaded. Please go to the Glassfish Installation page for details of how to install and configure Glassfish V2.

After the Glassfish is installed and Eclipse downloaded, go to the Eclipse and switch to the Java EE perspective. After closing all the unnecessary views, your environment would look something like this:


Now go to the Package Explorer and right click on the pan (If project Explorer is not there then you can add it by going to Window --> Show View --> Other and select project explorer from this menu) From the Right click menu select New --> Project. This will open a New Project Wizard. Select Enterprise Application Project on this screen and click next.


This will open New project creation screen.


Give a name to your project and select a Target Runtime, In our case we don't have one so click on New to create one.


Select the Glassfish V2 Java EE 5 from this list and click next. If you don't see this Runtime then click on the Download additional server adapters link and add Glassfish V2 on your list.


On this screen you need to provide the installation directory to the Glassfish server already installed on your machine. In my case the installation directory is C:\Software\Glassfish so I browsed to that directory. Now click the Finish button to return to your Project Configuration screen. You can now see you have Glassfish V2 Java EE 5 as your Target Runtime in the configurations.


No further configurations required at this point. Click Finish to return to your IDE.

Now you need to add the EJB project to this Enterprise Project. For that again right click on the package explorer pan and open the New Project Wizard as before.


This time select the EJB Project and click Next.


On this screen, give it a project name and Add the project to the Enterprise Project created earlier. Click Next for further configurations.


Remove the tick from Create EJB Client JAR option and make sure the Generate deployment descriptor is not ticked. We will do that later. Now click finish to create the project and go back to the IDE.

Now we need to create the web project for our interface. For that again right click on the Package Explorer view and open the new project wizard as before.


This time we need to select the Dynamic Web Project as our project type. Click on next on this screen after selecting the project type.


As before, give your project a meaningful name, add it to the EAR, and click next for further configurations.


Again, we don't need IDE to generate deployment descriptors so remove this option. Click on finish to go back to the IDE.

Last configuration that we need is to add JSF facets to our web project. For that right click on the Web Project and select properties. From the project properties screen select the Project Facets option.


On the Facets screen tick the Java Server Faces, and update the version to 1.2. As soon as you make these changes you will see another link appearing at the bottom of the screen asking you for Further Configurations. Click on this link to add JSF libraries.



You actually don't need to make any changes here, I changed the URL mapping from /faces/* to *.jsf just because it looks a little better to me. But that is my personal preference and it has nothing to do with the requirements. Click on OK to go back to the IDE.

Now you need to add the JSF libraries on the build path of your web project. Right click on the project and from the right click menu select Build Path --> Configure Build Path option. This will open the Java Build Path configuration wizard.


On this screen click on the Add External JARs... button and browse to your Glassfish lib directory and select the jsf-impl.jar from from there. This jar should already be there in the Glassfish lib directory, if not then download the file from Glassfish website. Click OK to finish the configurations.

All the configurations are now done. Let's start with creating the EJB project. Create a new package under the default ejbModule package in the EJB project. I created the package as com.test.session for the session EJBs.

Right click on the package and select New --> Other or select the package and hit Ctrl+N. This will open the Create New wizard.


Go down to the EJB folder and select Session Bean from the list and click Next.


Give your first EJB Class a meaningful name and click Finish. On the same screen you can tick the Remote check-box to create the remote interface for this session bean. Also you may want to create your Local and Remote interfaces in separate folders, I prefer this way as it is easier to manage when we have a large project. However, for this exercise, let's keep things as simple as possible.

When you click finish, it will create a Local interface and an session bean in the com.test.session package. Open the HelloEJBLocal interface and add a very simple business method.
package com.test.session;

import javax.ejb.Local;

@Local
public interface HelloEJBLocal {
 
 // Simple business method
 public String sayHello(String name);

}

Now implement the method in your Session EJB.

package com.test.session;

import javax.ejb.Stateless;

/**
 * Session Bean implementation class HelloEJB
 */
@Stateless
public class HelloEJB implements HelloEJBLocal {

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

 @Override
 public String sayHello(String name) {
  
  String msg = "EJB Says Hello ";
  return msg + name;
  
 }

}

A Very simple method. All we are doing here is taking the name of the caller in the parameter and saying Hello to the caller with her name.

Now let's start working on the Web Tier. I started by creating a Utility Class to communicate with all of my local EJB interfaces. For any Utility classes it's better to create a separate package for them. I created the package com.test.util under the src folder of my Web project.

Now create a utility class in the util package of your web project called BeanManagerFaces.java as follows

package com.test.util;

import javax.ejb.EJB;
import javax.el.ELContext;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;

import com.test.session.HelloEJBLocal;

public class BeanManagerFaces {

 
 public static String REAL_PATH;
 public static String CONTEXT_PATH;
 
 static{
  ServletContext context = (ServletContext)FacesContext.getCurrentInstance().getExternalContext().getContext();
  REAL_PATH = context.getRealPath("");
  CONTEXT_PATH = context.getContextPath();
 }
 
 public String getContextPath() {
  return CONTEXT_PATH;
 }

 public String getRealPath() {
  return REAL_PATH;
 }
 
 
 public Object getBean(String beanName){
  
  Object ejb;
  ELContext elContext = FacesContext.getCurrentInstance().getELContext();
  ejb = FacesContext.getCurrentInstance().getApplication().getELResolver().getValue(elContext, null, beanName); 
  return ejb;
 }
 
 @EJB
 private HelloEJBLocal helloEJBLocal;
 public HelloEJBLocal getTestEJB() {
  return ((BeanManagerFaces)getBean("beanManager")).helloEJBLocal;
 }

}

The most important line here is the line 42
  return ((BeanManagerFaces)getBean("beanManager")).helloEJBLocal;

Note the "beanManager" name, this is the name of this class when we add it to our list of Backing Beans in the Faces Config configurations. To do that go to the WebContact -->  WEB-INF folder of your web project and open the faces-config.xml file. Ideally Eclipse will open the file in the GUI Editor for faces-config file for you


On that configuration screen, go to the ManagedBeans tab and click the Add button.


Type in the fully qualified name of your utility class or click Browse button to locate your file. Now clock next to go to the next screen.


On this screen select the "session" option and give it the name "beanManager". Note here this is the same name that you specified on the Line 42 of BeanManagerFaces.java otherwise JSF will not be able to locate this bean.

If for whatever reason Eclipse fails to open the faces-config.xml in a UI editor then you can manually edit the faces-config.xml source to add these two Managed Beans as follows.
<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
    version="1.2">
 <managed-bean>
  <managed-bean-name>beanManager</managed-bean-name>
  <managed-bean-class>com.test.util.BeanManagerFaces</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
 </managed-bean>
 <managed-bean>
  <managed-bean-name>helloBean</managed-bean-name>
  <managed-bean-class>com.test.bean.HelloBean</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
 </managed-bean>

</faces-config>

Now create a Backing Bean for JSF with just two simple variables and one method as follows. It is a good idea to place all of the backing beans in a separate package for ease of management if you have a large project. I created the com.test.bean package to place all the backing beans.
package com.test.bean;

import com.test.service.HelloService;

public class HelloBean {
 
 private String name;
 private String mesg;
 
 public String sayHello(){
  return new HelloService().sayHello(this);
 }

 public String getName() {
  return name;
 }

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

 public String getMesg() {
  return mesg;
 }

 public void setMesg(String mesg) {
  this.mesg = mesg;
 }

}

Now we need to add this bean to our list of Managed Beans in the faces-config file. I gave it the name helloBean when adding it as a session bean in the faces-config.xml configuration. The process will be exactly the same as described above for BeanManagerFaces.java

Notice here that in the HelloBean we are delegating the actual processing to a separate service class to handle the business methods. Let's create this class now to handle the method details. I created a package com.test.service for all of my service classes. Now lets create the service class as follows:
package com.test.service;

import com.test.bean.HelloBean;
import com.test.session.HelloEJBLocal;
import com.test.util.BeanManagerFaces;

public class HelloService {
 
 public String sayHello(HelloBean bean){
  
  String name = bean.getName();
  String msg = null;
  
  // Get the local interface from bean manager faces
  HelloEJBLocal ejb = new BeanManagerFaces().getTestEJB();
  
  // Call the EJB method
  msg = ejb.sayHello(name);

  bean.setMesg(msg);
  
  return null;
 }
}


Now let's create a JSF page to test the EJB method. Right click on the WebContent directory and go to New --> Other and browse down to Web folder. Open the Web folder and select the JSP from there and click next. This is the same menu that you used previously to add Session EJB. Click next and select the WebContent directory and give a file name. I entered hello.jsp as my file name. Click next and it will show you options to select a template for your JSP.


Select the "New JavaServer Faces (JSF) Page (html)" from the options and click finish.This will create a JSF Template JSP file in the WebContent folder for you. Open the hello.jsp and add these lines:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="f"  uri="http://java.sun.com/jsf/core"%>
<%@ taglib prefix="h"  uri="http://java.sun.com/jsf/html"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<f:view>

 <h:form id="testForm">
  <table border="1">
   <tr>
    <td> <h:outputText value="Name:" /> </td>
    <td> <h:inputText value="#{helloBean.name}" /> </td>
    <td> <h:commandButton value="Say Hello" action="#{helloBean.sayHello}" /> </td>
   </tr>
   <tr>
    <td colspan="3"> <h:outputText value="#{helloBean.mesg}" /> </td>
   </tr> 
  </table>
 </h:form>
 
</f:view>
</body>
</html>

All Done, now let's export the project as EAR and deploy that to the Glassfish. For that right click on the Enterprise Project and click on Export from the menu. This will open the Export project Wizard. On this screen go to the Java EE folder and select the EAR File.


Click on Next, this will ask you for a location to save the file. Browse to whatever folder you want and save the file as TestProject.ear in your prefered location.

Now start the Glassfish server. Once you verify the Glassfish server is running then go to the Glassfish home directory --> domains --> test --> autodeploy

Test is the name of the domain I created for this exercise. Every domain has an autodeploy directory and all you need to do is to copy the TestProject.ear file to the autodeploy folder. Give it a few seconds to process the Enterprise Archive and once it is done then you will see TestProject.ear_deployed file appearing in this folder. This is the sign that Glassfish has deployed your EAR successfully. In case of any failures you will see the file deploy_failed appearing on this directory.

If you go to the server.log log file now, you will see the last line on the logs something like this:

/TestProjectWeb;|Initializing Mojarra (1.2_13-b01-FCS) for context '/TestProjectWeb'
[AutoDeploy] Successfully autodeployed : C:\software\glassfish\domains\test\autodeploy\TestProject.ear

This means your web application is successfully deployed and is initialized and waiting for requests at /TestProjectWeb

This is the root directory of your web project, i.e., WebContent. Since we placed our jsp file in the root directory, our fill URL including the domain name will look something like this:
http://localhost:8080/TestProjectWeb/hello.jsf

Notice the .jsf here. We are calling the hello.jsp as hello.jsf because in our previous steps we configured the jsf URLs to be *.jsf when we configured the Project Facets.

Let's go to this URL and see what happens.


Type in your name and click the Say Hello button to see if it is working ......


As you can see here. The EJB take the name entered in the box and returned with the response message.

Friday, November 16, 2012

Using Apache load balancer with Glassfish

Although Glassfish comes out of the box with provisions to add load balancing and failover but there are many reasons to add a third party web server to achieve that goal. For me the most compelling reason is the flexibility that this configuration provides. You can disconnect any glassfish domain and take it offline and then bring it back up again and connect it to the web server without anyone ever noticing something happened in the background.

Why use Apache web server? Now this is another question that can easily spark a debate but I am not going to go into the details, I am using Apache simply because this is easy to use and have been around since ages and has already proven as a sturdy and bulletproof server.

Just to illustrate the working, I shall use a very simple configuration, one Glassfish server with three domains running the same application and one Apache web server with mod_jk load balancing the requests evenly across all three domains.

 

So there we go, every request coming from the user is ending up on the load balancer and from there the load balancer splits these requests evenly across the three glassfish domains. If any of the domains fails then the load balancer will automatically split the load between the remaining two.


Prerequisites:
  • Install the Glassfish V2
  • Create the three domains with any http ports other than 80
  • Install Apache Web Server on port 80
You also need to download these files from Apache website
  • mod_jk.so
  • commons-logging.jar
  • commons-modeler.jar
  • tomcat-ajp.jar

Once the files are downloaded then place all three jar files in the Glassfish lib folder.

Place the mod_jk.so in the Apache Webserver’s modules folder.

Now create a workers.peroperties file and place it in the Apache Webserver’s conf folder.

Now you need to enable JK in the Glassfish Server for each domain. This is where you specify the port for mod_jk to use for load balancing. To enable that you have two options at your disposal

  • Update the domain.xml file
  • Use the asadmin Command Line Interface
You can you any oen of these approaches, whichever works for you. Let's see both of these options..

I am using port 9001 for my first domain and for the other three domains I am using 9002 and 9003 respectively.

  • Updating the domain.xml file

Open the domain.xml file and find the <java-config> tag and add the following line after the last <jvm-options> </jvm-options> tag within the java-config block just before the </java-config> tag.

-Dcom.sun.enterprise.web.connector.enableJK=9001

  • Using asadmin commands
Open the asadmin prompt and enter the following commands to create and enable the mod-jk listener

PS C:\Westcoast\glassfish4\bin> .\asadmin --port 4848
Use "exit" to exit and "help" for online help.
asadmin> create-http-listener --listenerport 9001 --listeneraddress 0.0.0.0 --defaultvs server mod-jk-listener
Command create-http-listener executed successfully.
asadmin> set server-config.network-config.network-listeners.network-listener.mod-jk-listener.jk-enabled=true
server-config.network-config.network-listeners.network-listener.mod-jk-listener.jk-enabled=true
Command set executed successfully.
asadmin>

Whichever way you choose, you need to repete the same steps to create mod-jk ports for all of your domains that you want to load balance. Depending on the Glassfish version, one of the above option nay not work.

NOTE: You need to use the asaadmin commands for Glassfish 4.x.x

Now open the workers.properties file and add the following lines:

# The advanced loadbalancer LB worker
# All request are processed by all workers

worker.list=loadbalancer

#First Worker for MyGlassfihsServer
worker.worker1.port=9001
worker.worker1.host=MyGlassfihsServerName
worker.worker1.type=ajp13
worker.worker1.lbfactor=33
# Define preferred failover node for worker11
worker.worker1.redirect=worker2

#Second Worker for MyGlassfihsServer
worker.worker2.port=9002
worker.worker2.host=MyGlassfihsServerName
worker.worker2.type=ajp13
worker.worker2.lbfactor=33
# Define preferred failover node for worker11
worker.worker2.redirect=worker3

#Third Worker for MyGlassfihsServer
worker.worker3.port=9003
worker.worker3.host=MyGlassfihsServerName
worker.worker3.type=ajp13
worker.worker3.lbfactor=34
# Define preferred failover node for worker11
worker.worker3.redirect=worker1

# Define the LB worker
worker.loadbalancer.type=lb
worker.sticky_session=false
worker.loadbalancer.balance_workers=worker1,worker2,worker3

I believe all the properties in this file are self-explanatory. You just need to create configurations for the workers and tell the mod_jk to balance all of the workers with the given load factor. The last line is where you tell the load balancer which workers it needs to balance, if you remove any worker from this line, apache will not forward any requests to that server.

Now open the httpd.conf file. You can see in the file quite a few lines where Apache is loading different modules. After the last LoadModule command, add your own lines to load the mod_jk.so module.

LoadModule jk_module modules/mod_jk.so
JkWorkersFile conf/worker.properties
# Where to put jk logs
JkLogFile "|bin/rotatelogs logs/mod_jk.log 86400"

# Set the jk log level [debug/error/info]
JkLogLevel error
# Select the log format
JkLogStampFormat "[%d-%b-%Y %H:%M:%S] "

# JkOptions indicate to send SSL KEY SIZE,
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
JkRequestLogFormat "%w %V %T"

# Send all jsp requests to GlassFish
JkMount / *.jsf loadbalancer

Notice the last line, i.e. “JkMount / *.jsf loadbalancer” This is where you are telling the Apache web server to load balance the requests. It will ONLY load balance the requests that have .jsf in the URL.

Now restart the glassfish domains to load the new jvm-options added to the java-config tag and then restart the apache web server.

You now have three Glassfish domains running behind an Apache Webserver and if you simply call the URL for your application, you will see from the Glassfish and Apache log files how this is splitting the load between these three domains.

For me the best thing is that if I need to take any of these domains down for whatever reason, all I need to do is to update this line in the workers.properties file

worker.loadbalancer.balance_workers=worker1,worker2,worker3

Remove the worker for that domain from the list of balanced workers and restart apache, which takes less than a second. Once I am ready to bring the domain live, I simply add that worker back to this line and restart apache.

If you find any difficulties then simply double check if you have all the files in the right places and also check for the syntax mistakes in the worker.properties file and the httpd.conf file in Apache. For Glassfish there is no further configuration required other than adding the enableJK JVM option.

Tuesday, June 12, 2012

Marshalling and Unmarshalling XML data using JAXB

JAXB is a Java Api for for Marshalling and UnMarshalling XML Data. The idea is that you bind an XML Schema to an object and then use this schema-bound object to marshall or unmarshall XML Data. Using this API you can transform any XML document into a java object and any java object into an XML document.

So how is it done. Here is a summery of the steps:

  • Create an XSD file that describes structure of your XML document
  • Execute xjc command from command prompt and pass package and .xsd file as parameters

Well that's it really, this will generate the java files in the given package. It will generate the complete package/directory structure according to the given parameter.

Let's try that using an example XSD file.


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <xs:element name="Contacts">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" ref="Contact"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="Contact">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="FirstName"/>
        <xs:element ref="LastName"/>
        <xs:element ref="PhoneNumber"/>
      </xs:sequence>
      <xs:attribute name="id" use="required" type="xs:integer"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="FirstName" type="xs:string"/>
  <xs:element name="LastName" type="xs:string"/>
  <xs:element name="PhoneNumber" type="xs:integer"/>
</xs:schema>

Save the above as contacts.xsd and then generate Java files using JAXB compiler. * xjc [file.xsd] -p [package]


> xjc contacts.xsd -p test.jaxb.contact
parsing a schema...
compiling a schema...
test\jaxb\contact\Contact.java
test\jaxb\contact\Contacts.java
test\jaxb\contact\ObjectFactory.java

This will create all the required classes for you in the given package. In our case you can see the package name is test.jaxb.contact so the classes are generated in this package. The compiler will also create necessory folders for you to place java fiels, i.e. all you need to do is to provide a package name and the rest is automatically handled.

Now let's do the Marshalling and UnMarshalling of some XML data using these generated java files. Marshalling means writing XML data and UnMarshalling means reading XML data. We will do the UnMarshalling first and the we will see how to do the Marshalling.

Un-Marshalling:

Now lets create a sample XML file and load the data in the XML file to our java objects. I am creating a sample file with some famous hollywood names.


<?xml version="1.0" encoding="UTF-8"?>
<Contacts>
    <Contact id="2002">
        <FirstName>Will</FirstName>
        <LastName>Smith</LastName>
        <PhoneNumber>0192824546</PhoneNumber>
    </Contact>
    <Contact id="2008">
        <FirstName>Bruce</FirstName>
        <LastName>Willis</LastName>
        <PhoneNumber>0138542756</PhoneNumber>
    </Contact>
    <Contact id="2029">
        <FirstName>Julia</FirstName>
        <LastName>Roberts</LastName>
        <PhoneNumber>0138452168</PhoneNumber>
    </Contact>
    <Contact id="2086">
        <FirstName>Kate</FirstName>
        <LastName>Hudson</LastName>
        <PhoneNumber>0169485324</PhoneNumber>
    </Contact>
</Contacts>

Now create a simple java file to Unmarshal this XML data. I am not going into the details of File Handlilng, here is a method that will take the InputStream that you can create for the file and then using that InputStream it will Unmarshal the XML data.


// Create an Input Stream for the XML file
// and pass the Stream as a method parameter
private static void unmarshalXMLData(InputStream is) throws Exception{

    // Context is the name of package 
    String context = "test.jaxb.contact";

    // Create an instance of JAXB Context
    JAXBContext jContext = JAXBContext.newInstance(context);


    // Unmarshal the data from InputStream
    Contacts contacts = (Contacts) jContext.createUnmarshaller().unmarshal(is);

    List contact = contacts.getContact();

    // Lets see the results.
    for (Contact c : contact) {
       System.out.println(c.id+""+c.firstName+""+c.lastName+""+c.phoneNumber);
    }
}

And when this method is executed, it produced the following results for the given XML file.


2002 Will Smith 192824546
2008 Bruce Willis 138542756
2029 Julia Roberts 138452168
2086 Kate Hudson 169485324

OK, so the Unmarshalling was easy, we successfully created some java objects and loaded data into these java objects using JAXB. Now let's see how to pulish the XML data from your java objects.

Marshalling:

Let's stick to the same conventions, just for the ease of use. To generate the XML data from our java classes, all we need to do is to tell the Marshaller what it needs to write and where.

Again I am not going into the details of File Handling, here is a method that takes the PrintWriter object as a parameter and writes the XML data to that writer.


private static void marshalXML(PrintWriter out) throws Exception{

  // Context is the name of package  
  String context = "test.jaxb.contact";
  // Initialise JAXB Context
  JAXBContext jc = JAXBContext.newInstance(context);

  // Always use factory methods to initialise XML classes
  ObjectFactory factory = new ObjectFactory();
  Contacts contacts = factory.createContacts();

  // Now create some sample contacts 
  Contact c = new Contact(); 
  c.setId(new BigInteger("2098"));
  c.setFirstName("Jonny");
  c.setLastName("Depp");
  c.setPhoneNumber(new BigInteger("2646215098"));
   
  // Always use the get methods for adding more objects to a collection
  contacts.getContact().add(c);
   
  c = new Contact();
  c.setId(new BigInteger("2168"));
  c.setFirstName("Anthony");
  c.setLastName("Hopkins");
  c.setPhoneNumber(new BigInteger("2646546879"));
  
  // Add the new Object to collection
  contacts.getContact().add(c);

  // Now Create JAXB XML Marshallar
  Marshaller m = jc.createMarshaller();
  m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );

  // Write the XML File
  m.marshal(contacts, out);

}

This method will initialize the Marshaller and will marshal the data to the PrintWriter. Fortunately PrintWriter is not the only option for you. You can also send data to any OutputStream or even to a ResultSet. Executing this piece of code will generate an XML file that will look something like this:


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Contacts>
   <Contact id="2098">
     <FirstName>Jonny</FirstName>
     <LastName>Depp</LastName>
     <PhoneNumber>2646215098</PhoneNumber>
   </Contact>
   <Contact id="2168">
     <FirstName>Anthony</FirstName>
     <LastName>Hopkins</LastName>
     <PhoneNumber>2646546879</PhoneNumber>
   </Contact>
</Contacts>

That's it really. From here on you can now create your own XSD documents to take advantage of JAXB. What I really like about that is that I dont have to worry about the XML formatting of my data when I am reading or writing XML data to an external resource. The JAXB API takes care of almost all of the possible scenarios where you have to manually handle the text in an XML document.

As far as XML is concirned, almost everyone now a days os familiear with it, however, not everyone feels comfortable with the XML Schema fiels (XSD). For those of you who are not comfortable with creating the XSD files, I would suggest using the XML to XSD converters. There are loads of converters available there on the internet if you search for the term XML to XSD.

My favourite is the trang-xml XML to XSD converter. This is a very simple jar file that you can use to generate an XSD document from any XML. Using Trang is very simple, all you need to do is to execute the following command from the command prompt and it will generate an XSD file for you from the given XML file.



java -jar trang.jar contacts.xml contacts.xsd

This sounds excellent, however, whenever you are using any XML to XSD generator, always use it with caution. The automated process that will identify the data types from your XML document to generate an XSD, is never going to be absolutely correct. There are always areas where you have to make neccessory changes to the XSD file after generating it using any convertor.