Tuesday, October 16, 2012

Terracotta and Tomcat Clustering - Page 3

Ok, our tutorial will end by adding Apache web server to load balance the traffic between both Tomcats.

install Apache Web Server 

just run the msi installer or install it from an RPM or from the OS repositories, i will refer to the installation directory as ${APACHE_HOME}. 

point your browser to localhost and make sure you are welcomed with the most famous message

It Works!


install mod_jk

extract the file tomcat-connectors-x.x.xx-windows-arch-httpd-2.2.x.zip, copy mod_jk.so to ${APACHE_HOME}\modules

Configure Apache Web Server


httpd.conf

edit the file ${APACHE_HOME}\conf\httpd.conf to add the following settings:
LoadModule jk_module modules/mod_jk.so
JkWorkersFile conf/workers.properties
JkShmFile logs/mod_jk.shm
JkLogFile logs/mod_jk.log
JkLogLevel info
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
JkMount /examples loadbalancer
JkMount /examples/* loadbalancer
JkMount /status status
JkMount /status/* status

here we are loading the mod_jk module, the mod_jk will load cluster members (Tomcats) from a file named workers.properties under ${APACHE_HOME}\conf directory.
also, any requests coming to apache at /examples will be forwarded to one of our Tomcats.
while /status will direct us to the mod_jk control panel.


workers.properties

create a new file under the directory ${APACHE_HOME}\conf named workers.properties with the following content:
# Define list of workers that will be used
worker.list=loadbalancer,status
# Define tomcat1
worker.tomcat1.port=8009
worker.tomcat1.host=127.0.0.1
worker.tomcat1.type=ajp13
worker.tomcat1.lbfactor=1
# Define tomcat2
worker.tomcat2.port=8010
worker.tomcat2.host=127.0.0.1
worker.tomcat2.type=ajp13
worker.tomcat2.lbfactor=1
# Load-balancing behaviour
worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=tomcat1,tomcat2
worker.loadbalancer.sticky_session=0
# Status worker for managing load balancer
worker.status.type=status

note that the worker names must match the jvmRoute configurations on the Tomcats. also, you can enable sticky sessions or disable to fit your need, however, i have turned it off here.

restart apache web server, if everything went as it should, navigating to http://localhost/examples would result in this page.

if you have debugging installed on your browser, like firebug, you can check your JSESSIONID value, you will find it post fixed with the jvmRoute value of the tomcat server who served the response.
if you hit the refresh button you can see that you are moving for every hit from one Tomcat to the other.

Testing Sessions Failover

point your browser to http://localhost/examples/servlets/servlet/SessionExample add a couple of values in the session.

now it is up to you, bring any Tomcat down (not both, of course) or any Terracotta (not both, of course), play around, your minimal setup is one Tomcat and  one Terracotta server.
your values in the session should never disappear!!!

Have Fun.


Terracotta and Tomcat Clustering - Page 2

Now, download apache-tomcat-6.0.35 and extract it twice to two directories of your choice, i will refer to them as ${CATALINA_HOME_NODE1} and ${CATALINA_HOME_NODE2}.

Copy Terracotta Libraries

we need two jar files to be copied to both Tomcats lib directory.
${TC_HOME}\common\terracotta-toolkit-1.6-runtime-5.0.0.jar
${TC_HOME}\sessions\terracotta-session-1.3.0.jar

Copy Terracotta Config File

we need to copy the same Terracotta configuration file to the conf directory of both Tomcats
${TC_HOME}\config\tc-config.xml

one small change to do, the client log location, both clients (Tomcat servers) can't be configured with the same client log location.

edit ${CATALINA_HOME_NODE2}\conf\tc-config.xml
change log location under the clients tag
<logs>%(user.home)/terracotta/client-logs</logs>
to be
<logs>%(user.home)/terracotta/client-logs2</logs>

Terracotta Valve

Tomcat connects to Terracotta by intercepting requests via a value, now let's hook that valve.
under the conf directory of both Tomcats, edit the file context.xml to look like this:
<Context>

 <!-- Default set of monitored resources -->
 <WatchedResource>WEB-INF/web.xml</WatchedResource>

 <!-- Uncomment this to disable session persistence across Tomcat restarts -->
    <Manager pathname="" />
 
 <!-- Hook the Terracotta Valve and configure it using the tc-config.xml file -->
 <Valve className="org.terracotta.session.TerracottaTomcat60xSessionValve" tcConfigUrl="../conf/tc-config.xml"/>

</Context>

note that we have disabled session persistence across Tomcat restarts because that is a task for Terracotta now.

Tomcat HA Configurations


jvmRoute


we need to define a unique jvmRoute for each Tomcat server.

edit ${CATALINA_HOME_NODE1}\conf\server.xml, change the Engine tag to look like this:
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">

edit ${CATALINA_HOME_NODE2}\conf\server.xml, change the Engine tag to look like this:
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat2">

Make sure you don't have port conflicts

as i am running both Tomcats on the same box, i had to change the ports for the second Tomcat.
in ${CATALINA_HOME_NODE2}\conf\server.xml, i have changed
shutdown port: 8005 to 8006
http port: 8080 to 8081
ajp port: 8009 to 8010

Run both Tomcats

open two more terminals and navigate to ${CATALINA_HOME_NODE1}\bin on one and ${CATALINA_HOME_NODE2}\bin on the other, on both terminals execute:
startup.bat

if everything went as it should, you should see something like that on both Tomcat terminals
Terracotta 3.7.0, as of 20120717-133013 (Revision unknown-20453 by cruise@rh5vmo113.terracotta.lan from 3.7.0)
Successfully loaded base configuration from file at 'H:\terracotta\apache-tomcat-6.0.35\bin\.\..\conf\tc-config.xml'.
Successfully loaded base configuration from file at 'H:\terracotta\apache-tomcat-6.0.35\temp\tc-config8509448930662041153.xml'.
Log file: 'C:\terracotta\client-logs\terracotta-client.log'.
Connection successfully established to server at 127.0.0.1:9510

Test it

Tomcat comes by default with a web application called examples, we will use it till the end of this tutorial to test our setup

point your browser to http://localhost:8080/examples/ and http://localhost:8081/examples/ and make sure both servers are responding.

open your Terracotta Development Console and make sure that both clients (the tomcat server) are present as below.


also note that the sessions button is now active.


Terracotta and Tomcat Clustering - Page 1

In this tutorial we will learn a lot of stuff :)
  1. How to install Terracotta in an Active/Passive HA (High Availability) model.
  2. How to configure Tomcat 6.0 to store/retrieve sessions on Terracotta.
  3. How to install and configure Apache Web Server configure it to relay requests to Tomcat.

Why?

i can do session replication using Tomcat built-in clustering module, so why do i use Terracotta?
well, the Tomcat clustering works just perfect with major four concerns:
  1. Clients' sessions data are part of the Tomcat JVM instance.
  2. There is extra work for Tomcat to make sure that the sessions and its data are replicated among the cluster
  3. not suitable for larger cluster because of using multicasting, imagine the network traffic generated by eight tomcat nodes replicating their sessions.
  4. You can't store an object which is not serializable.
So, moving this responsibility to Terracotta would help eliminating those four concerns.

Our Final architecture 


it is really simple:
  1. Both Tomcats store/retrieve sessions on the active Terracotta server.
  2. Apache web server forwards requests to both Tomcats.
  3. If the active Terracotta fails the passive Terracotta will be the active.

Let's Digg in

First things first! we need:
My five servers built on a single windows 7 box as a POC, you should have no troubles with running the same example on Linux, only minor chages are needed.

Install and Configure Terracotta

open a terminal and change the directory to the place where you downloaded terracotta-3.7.0-installer.jar
java -jar terracotta-3.7.0-installer.jar
you will see a setup wizard, it is straight forward, install Terracotta to the directory of your choice, from now on i will refer to that directory as ${TC_HOME}
now, go to ${TC_HOME} and create a new directory called 'config', this directory will contain our Terracotta configuration file that will set up the Terracotta cluster.
create a new file inside the config directory called tc-config.xml with the following content:
<tc-config xmlns="http://www.terracotta.org/config">
 <servers secure="false" xmlns:tc="http://www.terracotta.org/config" xmlns:con="http://www.terracotta.org/config" xmlns="">
  <server bind="0.0.0.0" host="127.0.0.1" name="node1">
   <data>C:\terracotta\server-data</data>
   <logs>C:\terracotta\server-logs</logs>
   <statistics>C:\terracotta\server-statistics</statistics>
   <dso-port bind="0.0.0.0">9510</dso-port>
   <jmx-port bind="0.0.0.0">9520</jmx-port>
   <l2-group-port bind="0.0.0.0">9530</l2-group-port>
   <data-backup>C:\terracotta\data-backup</data-backup>
   <index>C:\terracotta\server-data\index</index>
   <dso>
    <client-reconnect-window>120</client-reconnect-window>
    <persistence>
     <mode>temporary-swap-only</mode>
    </persistence>
    <garbage-collection>
     <enabled>true</enabled>
     <verbose>false</verbose>
     <interval>3600</interval>
    </garbage-collection>
   </dso>
  </server>
  <server bind="0.0.0.0" host="127.0.0.1" name="node2">
   <data>C:\terracotta\server-data2</data>
   <logs>C:\terracotta\server-logs2</logs>
   <statistics>C:\terracotta\server-statistics2</statistics>
   <dso-port bind="0.0.0.0">9511</dso-port>
   <jmx-port bind="0.0.0.0">9521</jmx-port>
   <l2-group-port bind="0.0.0.0">9531</l2-group-port>
   <data-backup>C:\terracotta\data-backup2</data-backup>
   <index>C:\terracotta\server-data\index2</index>
   <dso>
    <client-reconnect-window>120</client-reconnect-window>
    <persistence>
     <mode>temporary-swap-only</mode>
    </persistence>
    <garbage-collection>
     <enabled>true</enabled>
     <verbose>false</verbose>
     <interval>3600</interval>
    </garbage-collection>
   </dso>
  </server>
  <mirror-groups>
   <mirror-group>
    <members>
     <member>node1</member>
     <member>node2</member>
    </members>
   </mirror-group>
  </mirror-groups>
  <ha>
   <mode>networked-active-passive</mode>
   <networked-active-passive>
    <election-time>5</election-time>
   </networked-active-passive>
  </ha>
  <update-check>
   <enabled>true</enabled>
   <period-days>7</period-days>
  </update-check>
 </servers>
 <system xmlns:tc="http://www.terracotta.org/config" xmlns:con="http://www.terracotta.org/config" xmlns="">
  <configuration-model>production</configuration-model>
 </system>

 <clients xmlns:tc="http://www.terracotta.org/config" xmlns:con="http://www.terracotta.org/config" xmlns="">
  <logs>%(user.home)/terracotta/client-logs</logs>
  <modules>
   <module name="terracotta-toolkit-1.6" group-id="org.terracotta.toolkit"/>
  </modules>
 </clients>
</tc-config>
the important thing to note here is the servers node
  1. for each Terracotta server that will run in the cluster you need to define a server node.
  2. the server tag has three attributes:
    • bind: the default bind address which Terracotta listen to.
    • host: the IP address that will be used to connect to the Terracotta server.
    • name: the name of this node
  3. data: is where this server stores its data (make it unique for each server if you are running the cluster on one box)
  4. logs: is where the server store its logs (make it unique for each server if you are running the cluster on one box)
  5. statistics:  is where the server store its statistics (make it unique for each server if you are running the cluster on one box)
  6. port configurations: JMX, DSO and group ports (make it unique for each server if you are running the cluster on one box)
  7. data-backup: is where the server store its data backups  (make it unique for each server if you are running the cluster on one box)
  8. index: is where the server store its index files  (make it unique for each server if you are running the cluster on one box)
  9. dso: is the Distributed Shared Objects specific options.
we have now defined two servers, we need to define how they would work.
  1. mirror-group: a tag to define Terracotta groups 
    • members: a tag to add a server to a group using its name
  2. ha: is a tag to define the High Availability options of the group
    • mode: networked-active-passive it means that the communications between the servers will relay on networking.
    • election-time: the Terracotta server would wait for that time to decide if it should start as an Active or passive.
let's start out cluster an make sure everything is OK.
  1. open three consoles and navigate to ${TC_HOME}\bin on the three of them
  2. Start node1
    start-tc-server.bat -f ..\config\tc-config.xml -n node1
    
    you should see
    Becoming State[ ACTIVE-COORDINATOR ]
    Terracotta Server instance has started up as ACTIVE node on 0.0.0.0:9510 successfully, and is now ready for work.
    
  3. Start node2
    start-tc-server.bat -f ..\config\tc-config.xml -n node2
    
    you should see
    NodeID[127.0.0.1:9510] joined the cluster
    Moved to State[ PASSIVE-UNINITIALIZED ]
    Moved to State[ PASSIVE-STANDBY ]
    
  4. on the third console start the Terracotta Development Console
    dev-console.bat
    
    connect to either 127.0.0.1:9520 or 127.0.0.1:9521, you should see this screen


as per the screen shot, node1 is active and node2 is passive, play around by taking node1 down and see if node2 becomes the active and then Vice Versa.


Monday, October 8, 2012

Spring MVC, Security + Hibernate + DWR - Page 4



Spring MVC, Security + Hibernate + DWR, Let's PLay

OK, let's secure it ;)

The Spring security filter and Spring configuration:

in you web.xml edit the line to add a new file called spring-security.xml
<param-value>/WEB-INF/applicationContext.xml,/WEB-INF/dwr-context.xml,/WEB-INF/hibernate-context.xml,/WEB-INF/spring-security.xml</param-value>
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>
            org.springframework.web.filter.DelegatingFilterProxy
    </filter-class>
</filter>
<filter-mapping>
	<filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Filter Order

as we are now having two filters (springSecurityFilterChain and OpenSessionInViewFilter) we need to set the filter order as, 1-security 2-Hibernate.
make sure you filter order look like this
<filter-mapping>
	<filter-name>springSecurityFilterChain</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
	<filter-name>hibernateFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

spring-security.xml

this file will configure spring security, we will use form authentication, the users will be configured using this file too.
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/security
	http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
 
	<http auto-config="true">
		<intercept-url pattern="/*" access="ROLE_USER" />
	</http>
 
	<authentication-manager>
	  <authentication-provider>
	    <user-service>
		<user name="mtz" password="123" authorities="ROLE_USER" />
	    </user-service>
	  </authentication-provider>
	</authentication-manager>
 
</beans:beans>
the idea here is that we are securing every URL, with:
username:mtz
password:123

Test it

deploy the project and you should see the following page
and you are done, thanks :)
download the full example

Spring MVC, Security + Hibernate + DWR - Page 3



Spring MVC, Security + Hibernate + DWR, Let's PLay

OK, time for DWR ;)

the DWR servlet and Spring configuration:

in you web.xml edit the line to add a new file called dwr-context.xml
<param-value>/WEB-INF/applicationContext.xml,/WEB-INF/hibernate-context.xml,/WEB-INF/dwr-context.xml</param-value>
and add the DWR servlet and associate it with /dwr/* requests.
<servlet>
	<servlet-name>dwr</servlet-name>
	<servlet-class>org.directwebremoting.spring.DwrSpringServlet</servlet-class>
	<init-param>
		<param-name>debug</param-name>
		<param-value>true</param-value>
	</init-param>
</servlet>
<servlet-mapping>
	<servlet-name>dwr</servlet-name>
	<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

dwr-context.xml:

under /WEB-INF create a new file called dwr-context.xml, the file will configure the integration points between Spring and DWR.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.directwebremoting.org/schema/spring-dwr http://www.directwebremoting.org/schema/spring-dwr-3.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

       <dwr:configuration />
       <dwr:annotation-config id="dwrAnnotationConfig"/>
       <dwr:annotation-scan base-package="com.mtz.spring.dto"/>

</beans>
this file defines three configurations:
1- a default dwr configuration: the default configurations is fine as we are relying mostly on annotatios.
2- annotation config: DWR will scan spring beans for certain annotations.
3- annotation scan: DWR will scan the class path for certain annotations.


simply, if you have service class and want this service to be exposed to dwr you have two options, either, create a spring bean or let dwr scan its path, both ways, those classes had to have certain annotations.

the User class

as the User is under the package com.mtz.spring.dto and no Spring beans were (and mostly will never) created from that class, we need DWR to scan the classes under this package.
the class has been mentioned before, what needs to be explained
@DataTransferObject: DWR will marshal and unmarshal this object using a certain converter, in our example it is type="hibernate3", converter=H3BeanConverter.class, also for each property you need on the javascript object you have to expose it using the annotation @RemoteProperty

DAO and Service

in the package com.mtz.spring.dao

create the interface UserDAO

package com.mtz.spring.dao;

import com.mtz.spring.dto.User;
import java.util.List;

/**
 *
 * @author salemmo
 */
public interface UserDAO {
    public User getUser(int id);
    public User addUser(User user);
    public User removeUser(User user);
    public List<User> getAllUsers();
}

in the package com.mtz.spring.dao

create the class UserDAOImpl

package com.mtz.spring.dao;

import com.mtz.spring.dto.User;

import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

/**
 *
 * @author salemmo
 */
public class UserDAOImpl implements UserDAO{

    public User getUser(int id) {
        Session session = sessionFactory.getCurrentSession();
        User u = (User) session.get(User.class, id);
        return u;
    }

    public User addUser(User user) {
        Session session = sessionFactory.getCurrentSession();
        session.saveOrUpdate(user);
        return user;
    }

    public User removeUser(User user) {
        Session session = sessionFactory.getCurrentSession();
        session.delete(user);
        return user;
    }

    public List<User> getAllUsers() {
        Session session = sessionFactory.getCurrentSession();
        return session.createQuery("from User").list();
    }
    
    private SessionFactory sessionFactory;
    /**
     * @return the sessionFactory
     */
    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    /**
     * @param sessionFactory the sessionFactory to set
     */
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
    
}
in the package com.mtz.spring.service

create the interface UserService

package com.mtz.spring.service;

import com.mtz.spring.dto.User;
import java.util.List;

/**
 *
 * @author salemmo
 */
public interface UserService {
    public User getUser(int id);
    public User addUser(User user);
    public User removeUser(User user);
    public List<User> getAllUsers();
}
in the package com.mtz.spring.service

create the class UserServiceImpl

package com.mtz.spring.service;

import com.mtz.spring.dao.UserDAO;
import com.mtz.spring.dto.User;
import java.util.List;
import org.directwebremoting.annotations.RemoteMethod;
import org.directwebremoting.annotations.RemoteProxy;
import org.springframework.transaction.annotation.Transactional;

/**
 *
 * @author salemmo
 */
@RemoteProxy
public class UserServiceImpl implements UserService{

    
    @RemoteMethod
    @Transactional
    public User getUser(int id) {
        return getUserDAO().getUser(id);
    }

    @Transactional
    @RemoteMethod
    public User addUser(User user) {
        return getUserDAO().addUser(user);
    }
    
    @Transactional
    @RemoteMethod
    public User removeUser(User user) {
        return getUserDAO().removeUser(user);
    }

    @Transactional
    @RemoteMethod
    public List<User> getAllUsers() {
        return getUserDAO().getAllUsers();
    }
    
    private UserDAO userDAO;

    /**
     * @return the userDAO
     */
    public UserDAO getUserDAO() {
        return userDAO;
    }

    /**
     * @param userDAO the userDAO to set
     */
    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }
    
}

the important things to know about the UserServiceImpl are the annotations.
@RemoteProxy: tells DWR that you want this service to be exposed.
@RemoteMethod: tells DWR that you want this method to be exposed.
@Transactional: tells hibernate that you want this method to run in transaction.

Wrap it up in the applicationContext.xml

edit your applicationContext.xml to add the service bean
<bean class="com.mtz.spring.dao.UserDAOImpl" id="userDAO">
	<property name="sessionFactory" ref="sessionFactory"/>
</bean>

<bean class="com.mtz.spring.service.UserServiceImpl" id="userService">
	<property name="userDAO" ref="userDAO"/>
</bean>

edit hello.jsp

edit /WEB-INF/jsp/hello.jsp to make use of DWR
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">

	<%
		String contextPath=request.getContextPath();
	%>
	
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script type='text/javascript' src='<%=contextPath%>/dwr/engine.js'></script>
        <script type='text/javascript' src='<%=contextPath%>/dwr/interface/UserServiceImpl.js'></script>
        <script type='text/javascript' src='<%=contextPath%>/dwr/util.js'></script>
        <title>Welcome to Spring Web MVC project</title>
        <script type="text/javascript">
            function goGetit(){
                UserServiceImpl.getUser(document.getElementById("userid").value, {
                    callback:function(user) { 
                        document.getElementById("userage").value=user.age;
                        document.getElementById("username").value=user.name;
                    }
                });
            }
            
            function goSaveit(){
                var user= {
                    id: document.getElementById("userid").value,
                    age:document.getElementById("userage").value,
                    name:document.getElementById("username").value
                };
                UserServiceImpl.addUser(user, {
                    callback:function(user) { 
                        var useraction = (document.getElementById("userid").value=="")? "added":"altered";
                        document.getElementById("message").innerHTML = "User "+useraction+" with the ID: "+user.id;
                        getAllUsers();
                    }
                });
            }
            
            function goRemoveit(){
                var user= {
                    id: document.getElementById("userid").value
                };
                UserServiceImpl.removeUser(user, {
                    callback:function(user) { 
                        document.getElementById("userid").value="";
                        document.getElementById("userage").value="";
                        document.getElementById("username").value="";
                        document.getElementById("message").innerHTML = "User removed with the ID: "+user.id;
                        getAllUsers();
                    }
                });
            }
            
            function getAllUsers(){
                
                UserServiceImpl.getAllUsers({
                    callback:function(users) {
                        var cellFunctions = [
                            function(user) { return user.id; },
                            function(user) { return user.age; },
                            function(user) { return user.name; }
                        ];
                        dwr.util.removeAllRows("allusers");
                        dwr.util.addRows( "allusers", users, cellFunctions,{ escapeHtml:false });
                    }
                });
            }
        </script>
    </head>

    <body onload="getAllUsers()">
        <H2>Hello! This is the default welcome page for a Spring Web MVC project.</H2>
        <h3>${message}<h3/>
            <input type="text" id="userid"><input type="button" value="Get User By ID" onclick="goGetit();"/>
            <hr width="70%"/>
            <div id="message"> </div>
            <table>
                <tr>
                    <td>Age:</td>
                    <td><input type="text" id="userage"></td>
                </tr>
                <tr>
                    <td>Name:</td>
                    <td><input type="text" id="username"></td>
                </tr>
                <tr>
                    <td><input type="button" value="Add/Edit User" onclick="goSaveit();"/></td>
                    <td><input type="button" value="Remove User" onclick="goRemoveit();"/></td>
                </tr>
            </table>
            <hr width="70%"/>
            <table border="1">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>Age</th>
                        <th>Name</th>
                    </tr>
                </thead>
                <tbody id="allusers"> </tbody>
            </table>
    </body>
</html>

Test it

deploy the project and you should see the following page


Next - Configuring Spring Security

Sunday, October 7, 2012

Spring MVC, Security + Hibernate + DWR - Page 2


Spring MVC, Security + Hibernate + DWR, Let's PLay

OK, our Spring MVC should be working by now, let's add Hibernate ;)

create a table with the following specifications

MySQL table SQL script:


CREATE TABLE `user_` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `age` int(11) DEFAULT NULL,
  `name` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

now, configure your spring context to load a new file called hibernate-context.xml. edit your web.xml to add the mentioned file
<param-value>/WEB-INF/applicationContext.xml,/WEB-INF/hibernate-context.xml</param-value>

hibernate-context.xml:

create a new file called hibernate-context.xml under /WEB-INF with the following content
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

	<bean id="propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
		p:location="/WEB-INF/jdbc.properties" />

	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource"
		p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}"
		p:username="${jdbc.username}" p:password="${jdbc.password}" />

	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">

		<property name="dataSource">
			<ref bean="dataSource" />
		</property>

		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
				<prop key="hibernate.show_sql">true</prop>
			</props>
		</property>

		<property name="annotatedClasses">
			<list>
				<value>com.mtz.spring.dto.User</value>
			</list>
		</property>

	</bean>

	<bean id="transactionManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager"
		p:sessionFactory-ref="sessionFactory" />

	<tx:annotation-driven />

</beans>
basically, this file defines the following:
propertyConfigurer: spring will look for a properties file called jdbc.properties to replace the place holders mentioned here which are related to the database connection arguments.
dataSource: the datasource object will be used by the Hibernate session factory to connect to the database.
sessionFactory: the session factory that will be used by Hibernate, and it defines an attribute called annotatedClasses which are Hibernate mapping objects
transactionManager: we define this bean so we can be able to do database queries in transactions. Transaction Annotations: we will configure transactions via annotations.
alternatively, you can configure the session factory to scan the class path
<property name="packagesToScan" value="com.mtz.spring.dto"/>

jdbc.properties:

under /WEB-INF create a new file named jdbc.properties with the following content, please edit for your test
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=root

the User class

under the package com.mtz.spring.dto create a file named User.java
package com.mtz.spring.dto;

import java.io.Serializable;
import javax.persistence.*;
import org.directwebremoting.annotations.DataTransferObject;
import org.directwebremoting.annotations.RemoteProperty;
import org.directwebremoting.hibernate.H3BeanConverter;
/**
 *
 * @author salemmo
 */
@DataTransferObject(type="hibernate3", converter=H3BeanConverter.class)
@Entity
@Table(name="user_", catalog="spring")
public class User implements Serializable{
    @RemoteProperty
    private String name;
    @RemoteProperty
    private int age;
    @RemoteProperty
    private int id;

    /**
     * @return the name
     */
    @Column(name="name")
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the age
     */
    @Column(name="age")
    public int getAge() {
        return age;
    }

    /**
     * @param age the age to set
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * @return the id
     */
    @Id
    @GeneratedValue(strategy=javax.persistence.GenerationType.IDENTITY)
    @Column(name="id")
    public int getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }
}
here we define the mapping between this class and the database table using the annotations @Entity, @Table @Column and ID specific annotations (don't give so much attention to the @DataTransferObject and @RemoteProperty as they will be used by DWR later on)

OpenSssionInView Filter

we don't want to open session manually and maybe forget to close them, can we do it automatically? also, i have a lazy initialized property can't use it in my JSPs because the object sent to the view was de-attached from the Hibernate context. Well, Hibernate has a Filter called OpenSssionInView Filter that will solve both problems, and indeed, Spring has wrapped this filter. to enable this filter edit your web.xml to add
<filter>
	<filter-name>hibernateFilter</filter-name>
	<filter-class>
		org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
	</filter-class>
	<init-param>
		<param-name>sessionFactoryBeanName</param-name>
		<param-value>sessionFactory</param-value>
	</init-param>
</filter>

<filter-mapping>
	<filter-name>hibernateFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
of course the filter needs one session factory to create sessions.

Test it

no changes are expected, still we see our page, however, make sure that you did it right by not seeing any exceptions in the logs.

Next - Configuring DWR

Spring MVC, Security + Hibernate + DWR - Page 1


Spring MVC, Security + Hibernate + DWR, Let's PLay

OK, that is a lot, but it was fun getting those guys working together in harmony ;)

Why?
Spring has proven to be a solid playground and a unique platform, here how the parts will act:
So, first things first, Maven, all the artifacts(shiny name for a jar/war files) that are required in in this example relies on Maven, all but dwr, by the time of writing this blog, "At the time of our last development build (3.0 RC2) we did not have a process in place to upload artifacts to Maven Central", the dwr site says.
This issue is easy to fix, we will install dwr.jar manually to our local repository, even better, if you are using archiva; upload the artifact.
mvn install:install-file -Dfile=path/to/dwr.jar -DgroupId=org.directwebremoting -DartifactId=dwr -Dversion=3.0RC2 -Dpackaging=jar
By now we can go ahead and create a new project using maven archetype.
mvn archetype:generate
we will start with a plain java web archetype, choose maven-archetype-webapp or number 224 as per my Maven, i ended up with those parameters
groupId: com.mtz.spring
artifactId: SpringHibernateDWR
version: 1.0.0
package: com.mtz.spring

Enable Java web 2.5 specifications:

edit /WEB-INF/web.xml
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

Maven Dependencies:

edit the pom.xml to include the following dependencies
<dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>3.8.1</version>
 <scope>test</scope>
</dependency>

<dependency>
 <groupId>log4j</groupId>
 <artifactId>log4j</artifactId>
 <version>1.2.17</version>
</dependency>

<!--Spring-->
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-orm</artifactId>
 <version>3.0.6.RELEASE</version>
</dependency>
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-web</artifactId>
 <version>3.0.6.RELEASE</version>
</dependency>
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-webmvc</artifactId>
 <version>3.0.6.RELEASE</version>
</dependency>
<!--End Spring-->

<!--Spring Security-->
<dependency>
 <groupId>org.springframework.security</groupId>
 <artifactId>spring-security-core</artifactId>
 <version>3.0.7.RELEASE</version>
</dependency> 
<dependency>
 <groupId>org.springframework.security</groupId>
 <artifactId>spring-security-web</artifactId>
 <version>3.0.7.RELEASE</version>
</dependency> 
<dependency>
 <groupId>org.springframework.security</groupId>
 <artifactId>spring-security-config</artifactId>
 <version>3.0.7.RELEASE</version>
</dependency>
<!--End Spring Security-->

<!-- MySQL database driver -->
<dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <version>5.1.9</version>
</dependency>
<!-- End MySQL-->

<!--DWR-->
<dependency>
 <groupId>org.directwebremoting</groupId>
 <artifactId>dwr</artifactId>
 <version>3.0RC2</version>
 <type>jar</type>
</dependency>
<!--End DWR-->

<!-- Hibernate framework -->
<dependency>
 <groupId>org.hibernate</groupId>
 <artifactId>hibernate-core</artifactId>
 <version>3.6.10.Final</version>
</dependency>
<dependency>
 <groupId>org.hibernate</groupId>
 <artifactId>hibernate-ehcache</artifactId>
 <version>3.6.10.Final</version>
</dependency>
<dependency>
 <groupId>javassist</groupId>
 <artifactId>javassist</artifactId>
 <version>3.12.1.GA</version>
</dependency>
<!-- Hibernate library dependecy end -->

<!-- apache commons -->
<dependency>
 <groupId>commons-logging</groupId>
 <artifactId>commons-logging</artifactId>
 <version>1.1.1</version>
</dependency>
<!-- end apache commons -->
under the resources directory create two files.
log4j.properties
# Root logger option
log4j.rootLogger=DEBUG, stdout
 
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
logging.properties
org.apache.catalina.core.ContainerBase.[Catalina].level = DEBUG
org.apache.catalina.core.ContainerBase.[Catalina].handlers = java.util.logging.ConsoleHandler
i wanted to see a lot of loggings, so i set both to DEBUG
now build your project with maven and make sure everything is OK.
mvn clean package

Spring MVC:

in your web.xml add
  <context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
 <servlet-name>spring</servlet-name>
 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 <init-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/spring-mvc.xml</param-value>
 </init-param>
 <load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
 <servlet-name>spring</servlet-name>
 <url-pattern>*.html</url-pattern>
</servlet-mapping>
<welcome-file-list>
	<welcome-file>redirect.jsp</welcome-file>
</welcome-file-list>
this is basically,
1- initializing the the Spring context using ContextLoaderListener which will look for a file named applicationContext.xml under WEB-INF.
2- loads the Spring MVC dispatcher servlet, the dispatcher will look for a file named spring-mvc.xml under WEB-INF and it also associate *.html requests to it.
3- the container will use redirect.jsp as the default landing page.

applicationContext.xml

we will just create a bean-less file that will be used later on, create the file under /WEB-INF
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">


</beans>

spring-mvc.xml

create a file with that name under /WEB-INF with the following content
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <context:component-scan base-package="com.mtz.spring.mvc.controller"/>

    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/jsp/"
          p:suffix=".jsp" />

</beans>



this file configures Spring MVC to
1-Look for classes annotated with @controller in any class under com.mtz.spring.mvc.controller package.
2-add a basic view resolver that will map strings returned from the controller to a jsp file.

HelloController.java

under the package com.mtz.spring.mvc.controller create a new class called HelloController with the following content
package com.mtz.spring.mvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
@Controller
@RequestMapping("/welcome")
public class HelloController {
 
 @RequestMapping(method = RequestMethod.GET)
 public String printWelcome(ModelMap model) {
 
  model.addAttribute("message", "Spring + Hibernate + DWR, Hello World");
  return "hello";
 
 }
 
}
here, Spring MVC will bind /welcome.html requests to be handled by this controller, the controller will return its view name which is "hello" then the view resolver will map it to /WEB-INF/jsp/hello.jsp

hello.jsp

under /WEB-INF create a directory named jsp and then create a jsp file called hello.jsp with the following content
<%@page contentType="text/html" pageEncoding="UTF-8"%>

Hello! This is the default welcome page for a Spring Web MVC project.

Message: ${message}

this jsp will print the message that was sent from the controller.

redirect.jsp

create a new jsp under webapp directory with the following content
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<% response.sendRedirect("welcome.html"); %>
the redirect.jsp will redirect the requests from / to /welcome.html which will trigger the Spring MVC controller to work.

Test it

now package your project and deploy it, navigate to http://localhost:8080/SpringHibernateDWR/, if everything is correct, you should see this page.


Next - Configuring Hibernate