0 and 1’s

WebService Client Target NameSpace clash

Posted in J2EE, Java, Uncategorized by Rama Krishna on September 28, 2008

When handling multiple web services, you may have encountered scenarios where two web services declare schema definition under same namespace. Having multiple elements defined under same name space is not a problem. But, defining a same element with different structure under same namespace but in different web service can create problem.

This situation can happen when there is no global repository for the schema definitions in a company. I will explain the situation in more detail.

Consider a company XYZ with web address www.xyz.com. The company has two enterprise applications A and B. The application A decides to expose business function as web services. Two different teams 1 and 2 in application A are working on building the web service.

Both the team use the same target namespace http://www.xyz.org/appA/.

Team 1 builds an web service WS-1. They create a element NewOperation.

The schema of the element NewOperation and part of the wsdl definition is listed below


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.xyz.org/appA/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="NewWSDLFile" targetNamespace="http://www.xyz.org/appA/">
  <wsdl:types>
    <xsd:schema targetNamespace="http://www.xyz.org/appA/">
      <xsd:element name="NewOperation">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="in" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
   </wsdl:types>

Team 2 also builds an web service WS-2. They also create a element NewOperation.

The schema of the element NewOperation and part of the wsdl definition is listed below


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.xyz.org/appA/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="NewWSDLFile" targetNamespace="http://www.xyz.org/appA/">
  <wsdl:types>
    <xsd:schema targetNamespace="http://www.xyz.org/appA/">
      <xsd:element name="NewOperation">
        <xsd:complexType>
          <xsd:sequence>
              <xsd:element name="in" type="xsd:string" />
              <xsd:element name="in_1" type="xsd:string"></xsd:element>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
   </wsdl:types>

As you can see both the elements have the same name, but have different structure which is an not a good design. Since, both the team do not interact with other, they have failed to identify the flaw. Both, the team build server implementation of their web service and deploy each as EAR independent of the other. Both the teams do not face any issue, since both the server implementations are deployed independently.

Now, team in application B decides to use the web services provided by application A. They build client implementations for both the web services provided by A using standard build tools (clientgen from Weblogic). The build tools usually generate the java files (in a jar) binding the schema elements to classes created in the reverse namespace package (i.e., org.xyz.www.appA). They put the client jars of both the web service in the same classpath of the application B.

When they try to use the operations provided by the web service, they face exception related to serialization or deserialization whenever an operation involving the element NewOperation is involved. The exception can happen in either the WS-1 or WS-2 web service. This is because, a wrong class is used in the web service. As per java classpath rule, whenever NewOperation class needs to be loaded it finds only one version of the class depending on which client jar appears first. The other NewOperation class is never found by the application.

In order to resolve the issue, the team A needs to move the definition of the element NewOperation in one of the web service to a different target namespace. But this is a costly option, since WS-1 and WS-2 are already implemented and will require changes in the server implementation and also for all the clients using the web service.

The other option is for the client to use options provided in the build tools to generate class binding the schema definition in different package other than that generated by default for one of the web service.

This can done in Weblogic tool clientgen by adding the option typePackageName. Note the additional attribute typePackageName in the ant build script.


<clientgen wsdl="http://example.com/myapp/myservice.wsdl"
           packageName="myapp.myservice.client"
           clientJar="myapps/myService_client.jar"
       typePackageName="com.appB"
/>

Also the same can be done in Eclipse by checking the “Define custom mapping for namespace to package” option and defining the custom mapping when generating the client implementation of the web service.

This avoids the clash as the client implementations are generated under different package.

This problem needs to be avoided in first place when creating the web service. Each company needs to have a central repository where all the schema definitions are available to avoid duplication of element name under same target namespace.

Reblog this post [with Zemanta]

A J2EE application using JMS.

Posted in J2EE by Rama Krishna on February 13, 2008

About JMS.

The Java Message Service (JMS) API is a Java Message Oriented Middleware (MOM) API for sending messages between two or more clients. JMS is a part of the Java Platform, Enterprise Edition, and is defined by a specification developed under the Java Community Process as JSR 914.

- Source www.wikipedia.com

There are already a lot of resources about JMS on net. Hence, I will not elaborate more on this.

I have provided some links in the references section, which i found to be of great help in understanding JMS.

This post is about my experience of developing a simple JMS application using Glassfish Application Server. The IDE used is Eclipse. The messaging model is Point to Point.

The functionality of the application to be implemented.

This application provides a JMS consumer, which accepts ObjectMessage from the JMS producers. The ObjectMessage object encapsulates a User object. The User object is similar to objects in a typical business application. The JMS Consumer persists the User object. The JMS producer is a part of a typical online application, which sends the User object, whenever a new user registers for the service. And, the Consumer can be thought as a module, which performs certain operations with the User data with interfacing systems, which together provide a business work flow.

This the UML diagram of the User object.

classdiagram

The JMS Consumer just persists the User object in database.  The User class is part of a projBeans.jar jar file. Since, this class is common for both the server and client module, i have created a separate jar for the User Class.

Developing the server part of the application.

The server part of the application includes the JMS Consumer module, setting up connection factory and destination resource in the Glassfish application Server together with the database setup.

The web based admin console of the Glassfish application server can be accessed at port number 4848. Create a new JMS Connection factory persistUserConnectionFactory of type QueueConnectionFactory and a new JMS Destination Resource persistUserQueue of type Queue.

jms1

jms2

I have used EJB 3 Annotations to develop the Message Beans.

Create a New java project in Eclipse and implemented the following classes.

The JMS Consumer is the UserMessageListener Class, which is implemented as follows.


package com.ebeans.message;

import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import org.apache.log4j.Logger;
import com.beans.User;
import com.persist.db.PersistUser;

@MessageDriven(mappedName = "persistUserQueue")
public class UserMessageListener implements MessageListener{

	static Logger log = null;

	static {
 	log = Logger.getLogger(UserMessageListener.class);
 }

	public UserMessageListener() {
 }

	@Override
 public void onMessage(Message arg0) {
 	Object obj = null;
 	if (arg0 instanceof ObjectMessage) {
 		try {
 			obj = ((ObjectMessage)arg0).getObject();
 		} catch (JMSException e) {
 			e.printStackTrace();
 			log.error("Message could not be casted to ObjectMessage", e);
 		}
 		if (obj instanceof User) {
 			log.info("User object will be persisted." + ((User)obj).toString());
 			PersistUser persist = new PersistUser();
 			persist.persistToDb((User)obj);
 		}
 	}
 	else {
 		log.warn("ObjectMessage is not of type User." + obj.getClass());
 	}
 }

}

A class needs to implement the MessageListener interface to be a Message Bean, and hence the class UserMessageListener.The interface MessageListener has the following method, that needs to be overridden. This is the method, which is called by the JMS container when a message is received by the application server.

public void onMessage(Message arg0) {
}

This method includes the message processing logic. In this method, i create an instance of PersistUser and invoke the method persistToDb to persist the User object to database. The database used here is Derby.The implementation of the class PersistUser  is as follows.

package com.persist.db;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.sql.DriverManager;
import org.apache.log4j.Logger;
import com.beans.User;

public class PersistUser {

	static String dbDeryUrl = "jdbc:derby://localhost:1527/sample";

	static Logger log = null;

	static {
 	log = Logger.getLogger(PersistUser.class);
 }

	public PersistUser() {

	}
 public void persistToDb(User obj) {
 	Connection conn = null;
 	try {
 		Class.forName("org.apache.derby.jdbc.ClientDriver");
 		Properties prop = new Properties();
 		prop.put("user", "system");
 		prop.put("password", "manager");
 		StringBuilder sb = getSqlQuery(obj);
 		log.info("SQL = " + sb.toString());
 		conn = DriverManager.getConnection(dbDeryUrl, prop);
 		Statement st = conn.createStatement();
 		st.execute(sb.toString());
 		conn.close();
 	} catch (ClassNotFoundException e) {
 		log.error("JDBC Driver could not be found.", e);
 		e.printStackTrace();
 	} catch (SQLException e) {
 		log.error("SQL Exception." + e);
 		e.printStackTrace();
 	}
 	finally {
 		if (conn != null) {
 			try {
 				conn.close();
 			} catch (SQLException e) {
 				log.error("SQL Exception in finally block." + e);
 				e.printStackTrace();
 			}
 		}
 	}
 }

	private StringBuilder getSqlQuery(User obj) {
 	StringBuilder sb = new StringBuilder();
 	sb.append("INSERT INTO USERSINFO (first_name, last_name, middle_name, age, country) VALUES ('")
 	  .append(obj.getFname())
 	  .append("','")
 	  .append(obj.getLname())
 	  .append("','")
 	  .append(obj.getMname())
 	  .append("',")
 	  .append(obj.getAge())
 	  .append(",'")
 	  .append(obj.getCountry())
 	  .append("')");
 	return sb;
 }
}

I have used Log4j for logging. Also, included javaee.jar (part of Glassfish library), projBeans.jar (for User Class) , derbyClient.jar and apache-log4j-1.2.15 libraries.The structure of the project in Eclipse project explorer is as follows.project1Next, create the table with defination as follows in Derby database.

CREATE TABLE USERSINFO (
           FIRST_NAME VARCHAR(20),
           LAST_NAME VARCHAR(20),
           MIDDLE_NAME VARCHAR(20),
           AGE INTEGER,
          COUNTRY VARCHAR(20) )

The database is also ready. Next, we need to pack up the java project developed above as a Enterprise Archive (EAR) file.To create the ear file, create a new Enterprise Application Project under J2EE section of New Project Wizard and include the java project previously developed as part of this project.Next, create a directory lib under EarContent directory of the Enterprise Application Project and add the following libraries.

  • projBeans.jar
  • derbyClient.jar
  • apache-log4j-1.2.15

The structure of the Enterprise Application Project is as shown.

project2

Now, export this project as EAR file using the export option available in eclipse. Since,  J2EE 5 Annotation feature, there is no requirement of deployment descriptor file required in the EAR file.

The EAR file is ready for deployment in the Glassfish Application Server.

Use the Glassfish application server Web based admin console to deploy the EAR. When deploying the EAR, enable the “Run Verifier” option. With this option turned on, the application server verifies the EAR file and deploys, if successfully verified. If the verification fails, a message is displayed on the web console. To check the complete log, the logs is located in the following location.

%glassfish_home%/domains/%your_domain%/logs/server.log

The JMS Consumer is now successfully deployed.

Developing the client part (JMS Producer)

The jms producer which we will be creating runs as a separate standalone java process outside the application server. Hence, we cannot make use of Annotations provided by EJB3 for developing the jms producer module.

The implementation of the jms producer class UserJMSClient is as follows.

package com.client;

import javax.jms.JMSException;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.Session;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import com.beans.User;

public class UserJMSClient {
 InitialContext jndi = null;
 QueueConnectionFactory qFactory = null;
 Queue queue = null;
 boolean initialized = false;

	private void initialize() throws NamingException {
 	jndi = new InitialContext();
 	qFactory = (QueueConnectionFactory)jndi.lookup("persistUserConnectionFactory");
 	queue = (Queue)jndi.lookup("persistUserQueue");
 }

	public void sendObject(User user) throws Exception {
 	if (!initialized) {
 		try {
 			initialize();
 		} catch(NamingException e) {
 			e.printStackTrace();
 			throw new Exception("Jndi lookup failed");
 		}
 		initialized = true;
 	}
 	try {
 		QueueConnection qConn = (QueueConnection)qFactory.createQueueConnection();
 		Session session = qConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
 		ObjectMessage oMsg = session.createObjectMessage(user);
 		session.createProducer(queue).send(oMsg);
 	} catch (JMSException e) {
 		e.printStackTrace();
 		throw new Exception("sendObject Method Failure");
 	}
 }
}

The class uses the JNDI to obtain references to QueueConnectionFactory and Queue. Included the following jar files.

  • javaee.jar (%glassfish%\lib)
  • projBeans.jar
  • junit-4.4.jar
  • imqjmsra.jar (%glassfish%/lib\install\applications\jmsra)
  • appserv-rt.jar (%glassfish%\lib)
  • sun-appserv-ant.jar (%glassfish%\lib)

The following is the project structure in Eclipse.

project4

To test the UserJMSClient class,  i have used JUnit test case.

On executing the JUnit test case, the UserJMSClient object sent an User object to JMS consumer running in the application server. The JMS Consumer persisted the user object to the derby database, which i verified by querying the database.

project5

A simple JMS based application has been developed and verified successfully. This blog is more of practical implementation of JMS.

References
Resources
Source Code
Reblog this post [with Zemanta]