Pages

Thursday, July 19, 2012

Spring 3 Internationalization and Localization - Not 'Hello World', But 'Practical'

I wanted to add internationalization and localization feature provided by spring 3 to one of my current project recently. I went through the spring documentation and then searched on internet to find some resources. 
But I could not find a resource which was able to satisfy my client requirement. Most of the tutorials are like hello world application which gives basic understanding. Even spring documentation does not give in detailed explanation on integrating this feature to our own project. Expert developers can pick the stuff from spring documentation. But for others, have to put extra effort to make things up and running. With this tutorial, I am going to explain very practical scenario that most of the clients are expecting.

The requirement

I am using spring security with my application. User should be able to select the language from the log-in page which was specified as 'login-page' of spring security XML file. I have provided links as "English","Chinese","German" and "Spanish" on top right corner of my log-in page to select the language. User can select the language and log in to the system by providing username and password. Then the whole application should be from the selected language. And also when selecting the language from the log-in page, the contents of the log-in page should also be changed.

Spring configurations

As the first step, I had to configure LocaleChangeInterceptor interceptor with in the dispatcher-servlet.xml file. This XML file name will change according to the name given to DispatcherServlet in web.xml file. I have given 'dispatcher' as the name for DispatcherServlet. So I should create 'dispatcher-servlet.xml' file under /WEB-INF folder. My application is running on Tomcat 7.

I could not make it working by following the way of declaring this interceptor as in the spring documentation. The request for changing the locale before log in(ie: from the login page) was not intercepted by the locale change interceptor. Therefore, I had to declare it as fallows.

<mvc:interceptors>
  <mvc:interceptor>
     <mvc:mapping path="/doChangeLocale*"/>
         <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" >
             <property name="paramName" value="locale" />
         </bean>
    </mvc:interceptor>
</mvc:interceptors>

The 'LocaleChangeInterceptor' will intercept the request asking for locale change and the corresponding locale code will be stored in the session with the help of 'SessionLocaleResolver'.

Next we will look at how to declare the 'SessionLocaleResolver' in the 'dispatcher-servlet.xml' file.

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
     <property name="defaultLocale" value="en" />
</bean>

The SessionLocaleResolver will store the locale in the current session and resolve it for every subsequent user requests for the current session.

Next, we have to declare the message resource bean.

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="useCodeAsDefaultMessage" value="true" />
    <property name="basenames">
         <list>
            <value>classpath:messages</value>
         </list>
    </property>
    <property name="cacheSeconds" value="0" />
    <property name="defaultEncoding" value="UTF-8"></property>
</bean>

My application should support for 4 languages. So I added 4 property files into the 'resources' folder (ultimately all those property files should be in 'classes' folder) as follows.

messages_de.properties - German
messages_en.properties - English
messages_zh.properties - Chinese
messages_es.properties - Spanish

Note that, all the file names should start with the text which you specified as 'basenames' property of message resource bean.

The spring 3 security configurations were very important in this implementation. Keep in mind that, when you click any locale change link from the log-in page, you are not authenticated yet. But still that request should be intercepted by 'LocaleChangeInterceptor'. Otherwise, the language will not be changed as expected. There fore, any anonymous user should be allowed to make locale change request and that request should go through the 'LocaleChangeInterceptor'. 

Carefully look into my spring security configuration.
<http auto-config="false">
    <form-login login-page="/login.jsp"  authentication-failure-url="/login.jsp?login_error=true" default-target-url="/mainMenu.htm"/>
    <logout logout-success-url="/login.jsp"/>
    <intercept-url pattern="/doChangeLocale**" access="ROLE_ANONYMOUS,ROLE_ADMIN,ROLE_USER"/>
    <intercept-url pattern="/**" access="ROLE_ADMIN,ROLE_USER"  />
</http>

The login.jsp file is where user can log into the system by providing username and password and also that page has the corresponding links to change the locale. When user makes any request to a protected resource without authenticating, the user will be redirected to the login.jsp page. The above configuration says all the requests that are coming to the application should be from a authenticated user and also the user should be authorized except for the '/doChangeLocale**' request.

The intercept URL '/doChangeLocale**' is very important. Without that, the requests for changing locales are not intercepted by the locale change interceptor and finally locale will not change.

The followings are the locale change links that are placed in the login.jsp file.

<a href="<%=request.getContextPath()%>/doChangeLocale?locale=en">English</a>
<a href="<%=request.getContextPath()%>/doChangeLocale?locale=de">German</a>
<a href="<%=request.getContextPath()%>/doChangeLocale?locale=es">Spanish</a>
<a href="<%=request.getContextPath()%>/doChangeLocale?locale=zh">Chinese</a>


Hope this will be helpful for you.
You may also like:

Saturday, June 2, 2012

How to configure Apache HTTP server with Tomcat on SSL ?

Introduction

I recently needed to simulate our production deployment environment in my local machine. Our production applications are running on Apache HTTP server. We deploy application on multiple tomcat instances and also different Tomcats for different URL name spaces. Apache HTTP server is responsible as front door for all these. Apache HTTP server connects to the tomcat with mod_jk over mod_ssl. When Apache HTTP server receives a request, it checks for the requests and forward to the tomcat accordingly. This configuration is important for security and clustering.

This tutorial contains following section.
  1. Installing and configuring Apache HTTP server. 
  2. Installing and configuring Apache tomcat. 
  3. Installing and configuring mod_jk. 
  4. Configuring mod_ssl. 
  5. Testing the environment.

Installing and configuring Apache HTTP server.

Run the following command to download Apache HTTP server 2.2.22

wget http://mirrors.gigenet.com/apache//httpd/httpd-2.2.22.tar.bz2
wget http://www.apache.org/dist/httpd/httpd-2.2.22.tar.bz2.md5
md5sum -c httpd-2.2.22.tar.bz2.md5


After executing above command, httpd-2.2.22.tar.bz2 archive will be downloaded to your "Downloads" folder. Also you can directly download the apache server from the following location as well.

http://httpd.apache.org/download.cgi#apache22

To extract the zipped archive file, run the following command from /Downloads folder.

cd /home/semika/Downloads
tar -xjvf httpd-2.2.22.tar.bz2


Above command will extract the zipped archive file into httpd-2.2.22 folder under Downloads folder. Now, you should decide where you are going to install Apache HTTP server. I am going to install it to /home/semika/httpd-2.2.22 folder. You have to create the folder there. Navigate to your user folder and execute the following command to create new folder.

cd /home/semika
mkdir httpd-2.2.22

To install Apache to your particular platform, we need to compile the source distribution that we have already downloaded. If you see carefully inside the extracted folder under Downloads/httpd-2.2.22, you can see there is a configure script. We can compile the source distribution with that script and it will create necessary stuff to install Apache HTTP server.

When compiling Apache, various options can be specified that are suited to our local environment. For the complete reference of options provided, see here.

Since, we need mod_ssl to be configured with Apache compilation, we need to install OpenSSL development bundle. Otherwise, compilation will raise an exception. To install OpenSSL development libraries, run the following command.

sudo apt-get install openssl libssl-dev

Sometime, You might need to run the following command as well, if you encounter an error while apache compilation.

sudo apt-get install zlib1g-dev
sudo apt-get install libxml2-dev

To compile Apache source distribution, execute the following commands.

cd /home/semika/Downloads/httpd-2.2.22

./configure --prefix=/home/semika/httpd-2.2.22 --enable-mods-shared=all --enable-log_config=static --enable-access=static --enable-mime=static --enable-setenvif=static --enable-dir=static -enable-ssl=yes


--prefix                        : You can specify the installation directory
--enable-mods-shared : Setting this to 'all' will enable to install all the shared modules.
--enable-ssl                 : Since we are going to configure Apache HTTP server with mod_ssl, this has been set to 'yes' to compile Apache with mod_ssl. By default this option is disabled.

For other options specified in the configuration command, please look into full options reference documentation. After successfully running the above command, execute the following commands. Before execute the following command, just have look on your specified installation directory, ie:/home/semika/httpd-2.2.22, you can see that it is still empty.

make
make install

Now, you can see Apache HTTP server has been installed under /home/semika/httpd-2.2.22. Look for modules folder, you can see the list of modules installed. Confirm, whether it has installed mod_ssl.so. Now, you can start the Apache HTTP server.

cd /home/semika/httpd-2.2.22/bin
sudo ./apachectl start

If you see bellow line when executing the above command, 

"httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1 for ServerName" 

edit your /httpd-2.2.22/conf/httpd.conf file as follows. Look for "ServerName" property in httpd.conf file. You will see following line there.

#ServerName www.example.com:80

Uncomment this line and modify it as follows.

ServerName localhost

Again start the Apache HTTP server. If you did not change the default port which Apache server is running, check the following URL to check whether server is started or not. The default Apache HTTP server running port is 80. 

http://localhost/

With the above URL, if you see a page with "It works", you are done with Apache HTTP server.

Further, if you want to change the default port, you can edit the httpd.conf file as follows. Look for "Listen" property and change it as you wish.

I have set it to 7000. So my Apache HTTP server is running on 7000. I have to access the following URL to get "It works" page.

http://localhost:7000/

To stop Apache HTTP server,execute the following command.
cd /home/semika/httpd-2.2.22/bin
sudo ./apachectl stop

Installing and configuring Apache tomcat. 

Installing and configuring Apache tomcat is not a big thing, if you are involving with this kind of advance configuration. For the completeness of the tutorial, I will explain that a little. I am using Apache Tomcat 7.0.25. You can download it from Apache web site and extract it to some where in you local machine. After that, you have to set the environment variable as follows.

export CATALINA_HOME=/home/semika/apache-tomcat-7.0.25
export PATH=$CATALINA_HOME/bin:$PATH

You can start the tomcat with following command.

cd home/semika/apache-tomcat-7.0.25/bin
./startup.sh

If you want to see tomcat's console out put, execute the following commands before starting the tomcat.

cd home/semika/apache-tomcat-7.0.25/logs/
tail -f catalina.out 

After successfully starting the tomcat, try the following URL

http://localhost:8080/ 

By default, tomcat will run on port 8080. Now our Apache HTTP server is running on port 7000 and tomcat is on 8080. 

Further, to configure Tomcat with Apache HTTP server, we need to create workers.properties file under home/semika/apache-tomcat-7.0.25/conf/. 

workers.properties
# Define 1 real worker named ajp13
worker.list=ajp13

# Set properties for worker named ajp13 to use ajp13 protocol,
# and run on port 8009
worker.ajp13.type=ajp13
worker.ajp13.host=localhost
worker.ajp13.port=8009
worker.ajp13.lbfactor=50
worker.ajp13.cachesize=10
worker.ajp13.cache_timeout=600
worker.ajp13.socket_keepalive=1
worker.ajp13.socket_timeout=300

Apache HTTP server will connect to Tomcat through port 8009. If you see server.xml file under Tomcat's conf folder, you can see following connector declaration there.

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Installing and configuring mod_jk

If you look into httpd-2.2.22/modules folder, you can see mod_jk.so has been installed. The mod_jk is a connector for Apache HTTP server to connect to Apache Tomcat. To configure, you have to load mod_jk.so module to Apache HTTP server.

Edit /httpd-2.2.22/conf/httpd.conf file as follows. You need to add these properties.

# Load mod_jk module
# Update this path to match your modules location
LoadModule jk_module modules/mod_jk.so

# Where to find workers.properties
# Update this path to match your conf directory location
JkWorkersFile /home/semika/apache-tomcat-7.0.25/conf/workers.properties

# Where to put jk logs
# Update this path to match your logs directory location
JkLogFile /home/semika/apache-tomcat-7.0.25/logs/mod_jk.log

# Set the jk log level [debug/error/info]
JkLogLevel info

# Select the log format
JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"

# JkOptions indicate to send SSL KEY SIZE,
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories

# JkRequestLogFormat set the request format
JkRequestLogFormat "%w %V %T"

# Send everything for context /rainyDay to worker ajp13
JkMount /rainyDay ajp13
JkMount /rainyDay/* ajp13

I guess most of the properties defined above are clear for you. What is 'JkMount' and '/rainyDay'. The 'rainyDay' is one of my application deployed on Apache Tomcat. It says that "Forward all the request to the Apache Tomcat that are coming with /rainyDay namespace". 

With that, we have finished the configuration of mod_jk. 

Now, We will test the environment. Try the following URL's.

http://localhost:8080/rainyDay

Nothing special about the above URL. Since I have deployed my 'rainyDay' application on Apache Tomcat, We can access the application even without configuring with Apache HTTP server using mod_jk. 

Now, try the following URL.

http://localhost:7000/rainyDay 

If you can access the application with above URL, our configuration is successful. We know that, We have not deployed the 'rainyDay' application on Apache HTTP server, but on Apache Tomcat and also Apache HTTP server is running on port 7000. We can still access the 'rainyDay' application deployed on Apache Tomcat via Apache HTTP server.

Now just try the following URL.  

https://localhost:7000/rainyDay 

With the above URL, you can not access the application since URL has https protocol. To access the application with https://, we need to configure SSL with Apache HTTP server.  

Configuring mod_ssl.

To enable SSL on Apache HTTP server, again you have to edit httpd.conf file. Open this file and look for the following line. 

# Secure (SSL/TLS) connections

#Include conf/extra/httpd-ssl.conf


Uncomment the above line and open it. That is under /httpd-2.2.22/conf/extra/httpd-ssl.conf. Look for the following properties.

SSLPassPhraseDialog  builtin
SSLEngine on
SSLCertificateFile "/home/semika/httpd-2.2.22/conf/server.crt"
SSLCertificateKeyFile "/home/semika/httpd-2.2.22/conf/server.key"

Uncomment, if some are already commented out. Next, we have to generate SSL certificate files, server.crt and server.key. To generate these file, execute the following commands.

cd /home/sermika/Downloads
openssl genrsa -des3 -out server.key 1024
openssl req -new -key server.key -out server.csr
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

When executing above commands, it will ask for some details and also a password and you have to provide those. Also, you have keep this password in mind, because it will be needed to provide when starting Apache HTTP server.

To know more about the configuring SSL with Apache HTTP server, refer the this documentation. Now carefully look into /home/semika/Downloads folder. You can see the server.key and server.crt generated. You have to copy these two files into Apache HTTP server installation directory.

cd /home/semika/Downloads
cp server.crt /home/semika/httpd-2.2.22/conf/server.crt
cp server.key /home/semika/httpd-2.2.22/conf/server.key

Again open the httpd-ssl.conf file under /httpd-2.2.22/conf/extra. You can see </VirtualHost> element and some properties are defined within that element. Like we did in mod_jk configuration, here also, we have to declare the required application context URL's or any other URL's that are needed to be secured with SSL. You have to add JkMount declaration as follows.

</VirtualHost>
    ...........
    ...........
    JkMount /rainyDay ajp13
    JkMount /rainyDay/* ajp13
</VirtualHost> 

Now try the following URL's

https://localhost:7000/

This should load the "It works" page. I used the port 7000, because I have change the Apache HTTP server default port. You are successfully configured mod_ssl. 

Now try the following URL as well.

https://localhost:7000/rainyDay

You should be able to load the application.  

Testing the environment.

There is no any order of starting Apache Tomcat and Apache HTTP server. After starting your servers, you can test your configuration with following URL's.

http://localhost:8080/
If you see, Tomcat's home page, Tomcat configuration is successful.

http://localhost:8080/rainyDay
If you can access the application, you application is successfully deployed on Apache Tomcat. 

http://localhost:7000/
If you see "It works" page, Apache HTTP server is successfully configured.

http://localhost:7000/rainyDay
If it loads you application, mod_jk configuration with Apache HTTP server is successful. 

https://localhost:7000/
Again if you see "It works" page, mod_ssl configuration with Apache HTTP server is success. 

https://localhost:7000/rainyDay
Again if you can access the application, mod_ssl configuration with Apache HTTP server is success and Apache HTTP server properly handle all secure requests to 'rainyDay' application successfully.

References:

  1. How to configure Apache HTTP server with mod_ssl. 
  2. How To Install Apache 2 with SSL on Linux (with mod_ssl, openssl). 
  3. The Apache Tomcat Connector - Webserver HowTo. 
  4. Apache Module mod_ssl. 
  5. configure - Configure the source tree.

Friday, May 25, 2012

Enable https:// on tomcat in two steps.

With this tutorial, I will explain, how to enable SSL (Secure Socket Layer) on tomcat 7 in tow steps.
Step 01: Create .keystore file.

Run the following command to generate .keystore file from $JAVA_HOME/bin.

On windows :

%JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA 

On linux:

$JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA  

Step 02: Uncomment "SSL HTTP/1.1 Connector on port 8443" on $CATALINA_HOME/conf/server.xml and modify it as follows.

<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
              maxThreads="150" scheme="https" secure="true"
              clientAuth="false" sslProtocol="TLS" 
              keystoreFile="${user.home}/.keystore" keystorePass="123@com"/>


The "keystorePass" is the password which you provided when generating .keystore file. Now restart the tomcat.

By default port 8443 keeps listening https requests. Open a browser and check the following URL.

https://localhost:8443

If you see tomcat's home page, you are done.

Tuesday, May 22, 2012

How to expose an exsisting service as a web service ?

Introduction

This tutorial addresses a most practical scenario which is being faced by a developer. Most of the time, We may need to expose some of our existing services as web services. This situation can be encountered in different stages of project life cycle. If It is the initial stage, then You are almost safe and You can well prepare for that. But, What will happen, this requirement comes just after half of the development has finished or the system is running in production environment. 

This will be little tricky if the web services have not been taken into consideration for initial project architecture. You may involve with different kind of project architectures and use different kind of technologies. As a developer, You are not allowed to change some architectural stuff and configurations since there may be lots of dependencies. 

Most of the tutorials on the Internet explains the basic stuff of creating a web service. Some time, 'Hello world' application or sometime it may be simple calculator like that. These tutorials are good to have the basic understanding about the web services. But the real world scenario's are ten time complex than that and have to face difficulties when following those kind of tutorials. 

Practical scenario

With this tutorial, I am going to explain, How We really address a real world requirement that came through your supervisor. I am going to explain the same kind of scenario, which I faced recently. 

A health care organisation is running a plenty of pharmacies all around the island. They have a web application which handles all the inventories, pricing and billing, issuing pharmacy items etc. They needed to expose their pharmacy items prices through a web service so that their client application in the pharmacy can access those via the web service. 

Their web application has been developed within struts2, spring and hibernated integrated environment. It has all the spring managed DAO classes and also the service classes. The application uses spring's auto wiring technique, component scanning, transaction management etc. With these kind of background, I needed to expose pharmacy item prices as a web service. That is some of the methods from our current pharmacy service are needed to be exposed to outside via a web service.

I will show you, How to achieve this kind of requirement with a minimal modification to our existing project. 

Additional Libraries

I am going to implement the web service with JAX-WS. I have used JAX-WS 2.2 for my project. You can download the required JAX-WS version from here. This provides few tools that can be used to generate web service and it's client stuff. After downloading the required version of library, extract it some where in your local machine. I have placed it in my home folder. 

ie: /home/semika/jaxws-ri-2.2

Implementing web service

I already have spring managed service classes and DAO classes for pharmacy item which are being used internally by the web application. Those are not exposed to out side. Suppose, We need to expose findAll() method which returns a list of 'PharmacyItem' of 'PharmacyService' interface as a web service.

For Your convenience, I will show you the "PharmacyServiceImpl" java class which is used to perform normal pharmacy item operations. This is a usual spring mange bean. Keep in mind, methods of this class can only be used for internal operations of our web application currently. Those are not exposed as web services. 

PharmacyServiceImpl.java

/**
 * 
 */
package com.slauto.service.pharmacy.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.slauto.exceptions.ServiceException;
import com.slauto.model.pharmacy.PharmacyItem;
import com.slauto.persist.pharmacy.api.PharmacyItemDao;
import com.slauto.service.pharmacy.api.PharmacyItemService;

/**
 * @author semika
 *
 */
@Service("pharmacyItemService") 
public class PharmacyItemServiceImpl implements PharmacyItemService {

  @Autowired
  private PharmacyItemDao pharmacyItemDao;
 
  @Override
  public List<Pharmacyitem> findAll() throws ServiceException {
     return pharmacyItemDao.findAllPharmacyItems();
  }
}

As You can see, I have auto wired instance of 'PharmacyItemDao' with in the implementation class. As We all know, this is a spring managed service implementation class.

Next, We will implement the web service end point class for above spring managed service bean to expose it's methods as a web service methods. For the clarity, I created a separate class as web service end point.

PharmacyItemServiceEndPoint.java

package com.slauto.service.ws;
import javax.jws.WebMethod;
import javax.jws.WebService;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.slauto.exceptions.DataAccessException;
import com.slauto.model.pharmacy.PharmacyItem;
import com.slauto.service.pharmacy.api.PharmacyItemService;

/**
 * @author semika
 *
 */
@WebService(serviceName="pharmacyItemService") 
public class PharmacyItemServiceEndPoint {
 
  @WebMethod
  public List<Pharmacyitem> findAll() throws DataAccessException {
     ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 
     PharmacyItemService pharmacyItemService = (PharmacyItemService)context.getBean("pharmacyItemService");
     return pharmacyItemService.findAll();
  }
}

As You can see here, I am using "pharmacyItemService" bean which is used by our web application to access the relevant service method. The "pharmacyItemService" is a usual spring mange bean which is used to perform day to day pharmacy item operation. Nothing special on that. Specially note that @WebService and @WebMethod annotation which indicates this class works as a web service end point.

Here, I am getting service class instance via application context. Further, We can auto wire web service end point classes with spring by extending end point classes from "SpringBeanAutowiringSupport"  provided by spring. In that case, We do not need to create application context instance as I have done above. I could not make that work, that is why, I used the above technique.

With "SpringBeanAutowiringSupport", when deploying web service (explain bellow), I encountered an exception which was hard  for me to resolve that. So I choose this technique. Any way, I do not like, what I have used above :) .

Generating web service

I am using a apt, wsgen and wsimport tools provided by JAX-WS to generate the portable artefacts used in JAX-WS services. The relevant ant targets for the 'build.xml' file will be as follows. 

Your may need following property declared at the top of the 'build.xml' file.

Property:

<property name="tomcat.home"        value="/home/semika/apache-tomcat-7.0.25" />
<property name="jaxws.home"         value="/home/semika/jaxws-ri-2.2" />
<property name="build.classes.home" value="${basedir}/WEB-INF/classes" />
<property name="java.home"          value="/home/semika/java/jdk1.6.0_30">

Class path:

<path id="project.class.path">
   <pathelement location="${java.home}/../lib/tools.jar" />
   <fileset dir="${jaxws.home}/lib">
        <include name="*.jar" />
   </fileset>
   <pathelement location="${basedir}/WEB-INF/classes" />
   <fileset dir="${basedir}/WEB-INF/lib" includes="*.jar" />
</path>

JAX-WS apt tool target:

<target name="apt" depends="javac">
   <taskdef name="apt" classname="com.sun.tools.ws.ant.Apt">
       <classpath refid="project.class.path" />
   </taskdef>
   <apt fork="true"
        debug="true"
        verbose="true"
        destdir="${basedir}/WEB-INF/classes"
        sourcedestdir="${basedir}/WEB-INF/src"
        sourcepath="${basedir}/WEB-INF/src">
        <classpath>
            <path refid="project.class.path" />
        </classpath>
        <option key="r" value="${basedir}/WEB-INF" />
        <source dir="${basedir}/WEB-INF/src">
            <include name="**/*.java"/>
        </source>
   </apt>
</target>

If you want to know further about apt tool provided by JAX-WS, look into this. When running apt target, it scans the source path (src folder) and generates the required *.class and *.java files for the classes annotated with @WebService. In this case, for 'PharmacyItemServiceEndPoint.java'. If You look into the package where 'PharmacyItemServiceEndPoint' is in, You can see, it has a new package called 'jaxws' created. Inside that package, I could see following three java files. 

DataAccessExceptionBean.java
FindAll.java
FindAllResponse.java

These classes are generated by the tool and it varies based on your service implementation and dependent classes which you are involving to your web service. Actually, you don't need to much worry about these generated stuff.

Similarly, You can see the relevant *.class files under /WEB-INF/classes folder.

JAX-WS wsgen tool target:

<target name="wsgen" depends="apt"> 
    <taskdef name="wsgen" classname="com.sun.tools.ws.ant.WsGen">
         <classpath path="project.class.path"/>
    </taskdef>
  
    <wsgen 
          xendorsed="true"
          sei="com.slauto.service.ws.PharmacyItemServiceEndPoint"
          destdir="${basedir}/WEB-INF/classes"      
          resourcedestdir="${wsdl.dir}"
          sourcedestdir="${basedir}/WEB-INF/src"      
          keep="true"
          verbose="true"
          genwsdl="true">
          <classpath refid="project.class.path"/>
    </wsgen>
</target>

The wsgen tool will generate the WSDL file for our end point web service class. After running this target, have a look on ${wsdl.dir} location. You can see our WSDL file has been generated. If you want to know further about wsgen tool provided by JAX-WS, look into this. 

Deploying Web service

I wanted to deploy the web service with the usual server start up. SoI had to add the following configuration into the web.xml file.

<listener>
      <listener-class>
              com.sun.xml.ws.transport.http.servlet.WSServletContextListener
      </listener-class>
</listener>
<servlet>
      <servlet-name>pharmacyItemService</servlet-name>
      <servlet-class>
         com.sun.xml.ws.transport.http.servlet.WSServlet
      </servlet-class>
      <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
      <servlet-name>pharmacyItemService</servlet-name>
      <url-pattern>/pharmacyItemService</url-pattern>
</servlet-mapping>

As I told you before when deploying the web service with the end point class extended from "SpringBeanAutowiringSupport", it gives an exception. That is why, I decided to get the service bean via application context. If you manage this situation, please just post it.

And also, You need to create sun-jaxws.xml under the WEB-INF folder and declare the web service end point as follows.

<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">    
  <endpoint
      name="pharmacyItemService"
      implementation="com.slauto.service.ws.PharmacyItemServiceEndPoint"
      url-pattern="/pharmacyItemService"/>
</endpoints>

I am using apache tomcat 7.0.25 to deploy the web service. You will need to tell tomcat where it can find JAX-WS libraries when the tomcat is starting up. You can edit the 'catalina.properties' file located in CATALINA_HOME/conf folder. Look for the common.loader property. It will mostly look like follows.

common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar

Modify it as follows.

common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar,/home/semika/jaxws-ri-2.2/lib/*.jar

As You can see, I have added my JAX-WS library path at the end of comma separated list. These classes are made visible to both tomcat internal classes and to all web applications deployed in the tomcat container. Now, You can copy your .war file into tomcat's webapps folder and start the tomcat. Your web service should be deployed. To confirm your web service is deployed properly, You can check for it's WSDL. For WSDL file, You should check the URL in following format.

http://localhost:8080/<your context name>/pharmacyItemService?wsdl

If You can see your WSDL file on the browser, You are done, You have success fully deployed the web service.

Generating Web service client

Now, We have a deployed web service. Next, We will see how to write client class to access the web service with java program. You can create a simple java project and use the following build.xml file there. I have placed the complete build.xml file for client generation. I am using wsimport tool coming with JAX-WS to generate the web service client artefacts.
<?xml version="1.0" encoding="utf-8" ?>
<project name="WS-client" default="wsimport" basedir=".">
 
 <property name="jaxws.home"  value="/home/semika/jaxws-ri-2.2" />
 <property name="java.home" value="/home/semika/java/jdk1.6.0_30"/>
 
 <path id="jaxws.classpath">
  <pathelement location="${java.home}/../lib/tools.jar" />
  <fileset dir="${jaxws.home}/lib">
        <include name="*.jar" />
     </fileset>
 </path>
 
 <target name="wsimport"> 
  <taskdef name="wsimport" classname="com.sun.tools.ws.ant.WsImport">
        <classpath path="jaxws.classpath"/>
  </taskdef>
  <wsimport   xendorsed="true"
              debug="true"
       verbose="true"
              keep="true"
       destdir="src"
       package="com.slauto.service"
       wsdl="http://localhost:8080/slautomanage/pharmacyItemService?wsdl">
  </wsimport>
 </target>
</project>

If You want to know further about wsimport tool, you can look into this. After running the above target, just have a look on the package where you have specified under wsimport attributes. You will see set of java files generated. You can compile and bundle these into a single client.jar file and can be used with any of the java project which need this web service. 

I have created very simple java class to fetch the pharmacy items information through the web service. 

Client.java

package com.slauto.client;

import java.util.List;

import com.slauto.service.PharmacyItem;
import com.slauto.service.PharmacyItemService;
import com.slauto.service.PharmacyItemServiceEndPoint;

/**
 * @author semika
 *
 */
public class Client {

 /**
  * @param args
  */
  public static void main(String[] args) {
  
        PharmacyItemService p = new PharmacyItemService();
        PharmacyItemServiceEndPoint ep = p.getPharmacyItemServiceEndPointPort();
        List<PharmacyItem> pharmacyItems = ep.findAll();
  
        for (PharmacyItem pharmacyItem : pharmacyItems) {
           System.out.println(pharmacyItem.getCode()); 
        }
     }
  }

It is wonderful isn't it ?. You will see list of pharmacy item codes displayed just after running this class. These pharmacy information are coming through web service deployed by a web application developed with heavily complicated environment.
That is it from this tutorial. I hope this will be helpful for you and if you pick something up from this tutorial, don't forget to put a comment. 

Monday, May 14, 2012

Use primitives over boxed primitives whenever you have the choice - Java

Just with the reading of this post's title, some of you may be confused because, some of the java developers have used to use boxed primitives (Wrapper class objects of primitives) without a specific reason. But, We should be care of using primitives rather than the boxed primitives when We have a choice. I will explain Why, We should choose primitives.

There are differences between primitives and boxed primitives though You did not pay attention too much on that.

Primitives are simpler and faster, and they have only their values. Primitives are generally more time- and space-efficient than boxed primitives. But the boxed primitives have different identities though they have same value. Consider the following code which most of the java developers are well known and understandable.

 Integer first = new Integer(41);
 Integer second = new Integer(41);
  
 if (first == second) {
      System.out.println("Equal");  
 }

As You well known, the above code will not print the "Equal" since it performs an identity comparison on the two object references. Applying == operator on boxed primitives always gives wrong output. To fix the above problem, either You can use Integer's equal() method or getting integer values from Integer's intValue() method. Now, You consider the following code.

Integer first = new Integer(41);
Integer second = new Integer(41);
       
if (first > second) {
     System.out.println("First is greater than Second");  
} else {
     System.out.println("First is not greater than Second");
}

Can you spot the output of above code? The above code will print the else condition. In this case, it did not perform the identity comparison like == comparison. It compared the actual values and gave the correct output. So When evaluating this kind of comparison, Integer instances are auto-unboxed;that is, it extracts their primitive values. I know, You have used this kind of comparison plenty of times. Think, You really knew about this. So We must choose primitives or boxed primitives when We have a choice. 

Next, We will move to another difference. You know that, When We declare a variable with primitive type, it always has functional values. But boxed primitives can have 'null' values which sometime can raise exception. Consider the following example.


package com.semika.mail;

public class BoxPrimitiveTest {
 
static Integer x;
static int y;

public static void main(String[] args) {

   if (x == 0) {
        System.out.println("X is Zero");  
   }
  
   if (y == 0) {
        System.out.println("Y is Zero");  
   }
}
}

What will be the output of the above program. The above code will not run and it will throw an exception. As You can see, variable 'x' is declared with boxed primitive type. It has 'null' value when evaluating the x==0 expression. When a null object reference is auto-unboxed,You get a NullPointerException. 

Next, I will explain the harm of mixing both box primitives and primitives in a single operation. When You mix primitives and boxed primitives in a single operation, the boxed primitive is auto-unboxed which results severe performance problems. Consider the following class.

class LineItem {
 
double price;

public double getPrice() {
    return price;
}

public void setPrice(double price) {
    this.price = price;
}
}

Suppose, You want to iterate list of 'LineItem' and calculate the total value. Note that 'price' variable is declared with a primitive type. Look into the following code of calculating total cost.

Double totoalCost = 0.0; 
  
for (LineItem lItem : lineItemList) {
    totoalCost += lItem.getPrice();
}

The above code makes a server performance problem. Since, You have declared 'totoalCost' variable to be of the boxed primitive type Double instead of primitive type double. The program compiles and run without an error or warning.But the variable is repeatedly boxed and unboxed, causing the observed performance degradation.

Finally, the developer should never ignore the distinction between primitives and boxed primitives. You have to choose what to use.
Share

Widgets