 |
hibernar.org
A friendly forum for Hibernate users
|
<< Back to Articles
Spring-Hibernate example
A minimal web application demonstrating Spring integration with Hibernate.
Hibernate is a decent persistent library, but when used in a web environment, sometimes it is difficult to
manage the lifecycle of the basic Hibernate object, the Session.
Because of this, many novel Hibernate programmers are plagued by errors like "session closed",
"LazyInitializationException", "no transaction" in their first Hibernate applications.
Fortunately, there is another set of libraries, the Spring, framework, that comes to the rescue.
Spring takes care of all the wiring required to wrap your database code in transactions, and manage your Hibernate sessions in a web application.
This article will show you how to build a simple Spring/Hibernate application from scratch, no previous knowledge of either tool is assumed.
Our sample web application will use Spring 2.5.5 and Hibernate 3.2.2 to maintain a simple database with one table of singers:
(The SINGER_ID key will be autogenerated).
The web interface will also look extremely simple:
Typing a singer's name and clicking on "Add Singer" immediately adds it to the list and displays the list.
The "Search" button limits the list to only those singer containig a given, substring regardless of case.
Clicking the "Delete" button next to each singer, deletes that singer from the database, and relists the rest.
And this is the directory structure of the web application, once it has been deployed.
myWebApp
`-- WEB-INF
`-- web.xml
|-- classes
| `-- org
| `-- hibernar
| `-- beans
| `-- business
| `-- controllers
|-- lib
`-- views
In the /classes/org/hibernar/beans folder, we put two files, our bean (Singer.java) and a mapping configuration file, Singer.hbm.xml.
They look like this:
Singer.java
package org.hibernar.beans;
import java.io.Serializable;
public class Singer implements Serializable{
private String id;
private String name;
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
Singer.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.hibernar.beans.Singer" table="SINGER">
<cache usage="read-only"/>
<id name="id" column="SINGER_ID" type="string">
<generator class="uuid"/>
</id>
<property name="name" column="SINGER_NAME" type="string"/>
</class>
</hibernate-mapping>
Business code
Our application will have just one business class, which will contain all our Hibernate-specific code: SingerBoImpl.
It is a common practice to "decouple" the business code into an interface that contains the business methods, and an implementation class.
Whenever possible, for references outside these 2 files, the interface (SingerBo) should be used.
As this is a very simple application we will not create 2 separate layers for "business" and "DAO" object. All our Hibernate data acces logic will reside directly on the business object,
as it is indeed common in many Hibernate applications.
/src/org/hibernar/business/SingerBo.java
package org.hibernar.business;
import java.util.List;
import org.hibernar.beans.Singer;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Transactional(propagation=Propagation.REQUIRED)
public interface SingerBo {
List getByName(String partOfName);
void delete(String singerId);
void add(Singer singer);
}
It looks like a normal interface, with 3 business methods. The only uncommon thing is that @Transactional annotation.
That annotation (plus some very little extra configuration elsewhere) allows all the methods of this interface to be wrapped in database transactions.
The implementing class, SingerBoImpl, contains normal Hibernate code.
Notice that I am referring to this class as a "business object", but the Hibernate code is actually "data access code".
It is very common for Hibernate applications to unite both layers (business object and data access object) into one, especially for a simple application like this one.
/src/org/hibernar/test/business/SingerBoImpl.java
package org.hibernar.business;
import java.util.List;
import org.hibernar.beans.Singer;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
public class SingerBoImpl implements SingerBo {
private SessionFactory sessionFactory;
public void add(Singer singer) {
Session session=SessionFactoryUtils.doGetSession(sessionFactory, false);
session.persist(singer);
}
public void delete(String singerId) {
Session session=SessionFactoryUtils.doGetSession(sessionFactory, false);
Query query=session.createQuery("delete from Singer where id='" + singerId + "'");
query.executeUpdate();
}
@SuppressWarnings("unchecked")
public List getByName(String partOfName) {
StringBuffer sql=new StringBuffer("select sin \n");
sql.append("from Singer sin \n");
sql.append("where upper(sin.name) like '%" + partOfName.toUpperCase() + "' \n");
Session session=SessionFactoryUtils.doGetSession(sessionFactory, false);
Query query=session.createQuery(sql.toString());
return query.list();
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}
The Spring framework (or more specifically, the part of Spring used for web applications, called Spring MVC), uses a type of classes
called "Controllers" for processing the user's HTTP requests and controlling the flow of the application.
Controllers receive parameter data from the user interface (in our case, a simple JSP), and call business objects as needed.
In our case, after the controller did what he had to do, it redirects the flow of the application back to our single JSP.
The following, simple Spring Controller uses only one method called handleRequestInternal() to process all requests. What we do exactly,
after we get the request, depends on one request parameter: "actionName". For those familiar with Struts, a Spring Controller is similar to a
Struts Action.
/src/org/hibernar/controllers/SingerController.java
package org.hibernar.controllers;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.hibernar.beans.Singer;
import org.hibernar.business.SingerBo;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
public class SingerController extends AbstractController {
private static final Logger logger = Logger.getLogger(SingerController.class.getName());
private SingerBo singerBo;
@SuppressWarnings("unchecked")
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
Map model=new HashMap();
String actionName=request.getParameter("actionName");
if (actionName!=null){
//adding
if (actionName.equals("add")){
String name=request.getParameter("nameToAdd");
Singer sin=new Singer();
sin.setName(name);
singerBo.add(sin);
}
//deleting
if (actionName.equals("delete")){
String id=request.getParameter("id");
singerBo.delete(id);
}
}
//searching (this happens every time)
String nameToSearch=request.getParameter("nameToSearch");
if (nameToSearch==null) nameToSearch="";
List singers=singerBo.getByName(nameToSearch);
model.put("singers", singers);
ModelAndView mav = new ModelAndView("singers", model);
return mav;
}
public void setSingerBo(SingerBo singerBo) {
this.singerBo = singerBo;
}
}
Notice that, at the end of the controller method, we return a Spring object called ModelAndView. This object simply consist of a Map,
(which holds results data that will be readable in our JSP at a request scole), and a reference to which "view" or JSP the flow of the application
must forward.
Notice also, how our business object (or, more precisely, the interface SingerBo that our business object implements), is a member variable of the Controller.
The JSP is straigthforward, it uses the JSTL library, a couple of hidden fields, and some minimum Javascript to submit from different actions.
WEB-INF/views/singers.jsp
<%@ page language="java" session="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<html>
<head>
<script type="text/javascript">
function addSinger(){
var form=document.forms[0];
form.actionName.value='add';
form.submit();
}
function deleteSinger(id){
var form=document.forms[0];
form.actionName.value='delete';
form.id.value=id;
form.submit();
}
function searchSinger(id){
var form=document.forms[0];
form.actionName.value='search';
form.submit();
}
</script>
</head>
<body>
<form method="post" action="singers.do">
<input type="hidden" name="actionName" />
<input type="hidden" name="id" />
<table border="1">
<tr>
<td><input type="text" name="nameToAdd" size="20"/> </td>
<td><input type="button"name="add" value="Add Singer" onclick="addSinger()"/> </td>
</tr>
<tr>
<td><input type="text" name="nameToSearch" size="20"/> </td>
<td><input type="button" name="search" value="Search" onclick="searchSinger()"/> </td>
</tr>
<c:forEach items="${singers}" var="singer">
<tr>
<td><c:out value="${singer.name}"/></td>
<td><input type="button" name="delete" value="Delete Singer" onclick="deleteSinger('<c:out value="${singer.id}"/>')" /></td>
</c:forEach>
</table>
</form>
</body>
</html>
Notice how the object "singers" (a list of beans that we had previously put in our ModelAndView Map), is available at the default (request) scope.
Configuration files
The central part of any Spring application is the Application Context, an object in memory that contains all the relevant objects of our application, normally as singletons.
Our business object SingerBo, and our controller SingerController, for example, will be contained within this application context.
The Application Context also fulfills the function of wiring together our application objects, setting some objects (via simple setter properties) as member variables of other objects.
In our case, for example, the SingerBo is a member variable of SingerController, so the Application Context instantiates one object of the class SingerBoImpl,
another object of the class SingerController, and sets the singerBo object into the singerController object, so that the controller has a reference to the business object it can call.
For instantiating and wiring together these objects, Spring uses one or more xml configuration files. Here's the xml fragment that instantiates our objects and sets one as a property to the other:
<bean id="singerBo" class="org.hibernar.business.SingerBoImpl">
...
</bean>
<bean id="singerController" class="org.hibernar.controllers.SingerController">
<property name="singerBo" ref="singerBo"/>
</bean>
In our example, we will use 2 of these xml files in order to build our application context. One , applicationContext.xml, will contain our objects and core functionality.
The other, typically called ui-servlet.xml, will contain web-related functionality.
This is our applicationContext.xml file, the core of our application. I will explain one by one the different parts of it.
<?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: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-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="WEB-INF/classes/video-svc.properties" />
</bean>
<bean id="mainDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${db.driver}" />
<property name="jdbcUrl" value="${db.uri}"/>
<property name="properties">
<props>
<prop key="c3p0.acquire_increment">1</prop>
<prop key="c3p0.idle_test_period">100</prop>
<prop key="c3p0.max_size">15</prop>
<prop key="c3p0.max_statements">0</prop>
<prop key="c3p0.min_size">1</prop>
<prop key="user">${db.user}</prop>
<prop key="password">${db.pass}</prop>
</props>
</property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="mainDataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${db.hibernate.dialect}</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.show_sql">true</prop>
<prop key="hibernate.max_fetch_depth">4</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.cache.provider_configuration_file_resource_path">WEB-INF/classes/ehcache.xml</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<!-- stats -->
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.cache.use_structured_entries">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>org/hibernar/test/beans/Singer.hbm.xml</value>
</list>
</property>
</bean>
<bean id="singerBo" class="org.hibernar.business.SingerBoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="mainTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven transaction-manager="mainTransactionManager"/>
</beans>
The most recent version of Spring uses XSL stylesheets instead of dtd. These are their declarations.
Don't worry too much about this, it is one of those things that you customarily copy from project to project.
<?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: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-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
Then comes the data source. The Application Context configuration file is an excellent place to put our database connection information.
We are obtaining a connection pool using a well knonw library: c3p0 (Spring gives you the choice of using other connection pooling libraries as well).
<bean id="mainDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${db.driver}" />
<property name="jdbcUrl" value="${db.uri}"/>
<property name="properties">
<props>
<prop key="c3p0.acquire_increment">1</prop>
<prop key="c3p0.idle_test_period">100</prop>
<prop key="c3p0.max_size">15</prop>
<prop key="c3p0.max_statements">0</prop>
<prop key="c3p0.min_size">1</prop>
<prop key="user">${db.user}</prop>
<prop key="password">${db.pass}</prop>
</props>
</property>
</bean>
We could have hard-coded our database connection properties (db.user, db.pass, etc.), Instead, it is more convenient to "externalize" those properties
into a separate properties file, just in case we plan to use a different database in the future. Spring allows you to do this, just by adding an instantiation of
an object called "propertyConfigurer" to your configuration file:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="WEB-INF/classes/video-svc.properties" />
</bean>
By the way, a typical properties file can contain other "externalizable" properties as well. For example, logging properties.
WEB-INF/classes/singers.properties
log.dir=/logs
log.filename=singer
db.user=myuser
db.pass=mypassword
db.driver=com.mckoi.JDBCDriver
db.uri=jdbc:mckoi://localhost/
db.hibernate.dialect=org.hibernate.dialect.MckoiDialect
The next part of our applicationContext.xml file is more Hibernate-specific: the SessionFactory.
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="mainDataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${db.hibernate.dialect}</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.show_sql">true</prop>
<prop key="hibernate.max_fetch_depth">4</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.cache.provider_configuration_file_resource_path">WEB-INF/classes/ehcache.xml</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<!-- stats -->
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.cache.use_structured_entries">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>org/hibernar/test/beans/Singer.hbm.xml</value>
</list>
</property>
</bean>
And notice how, again, we can refer to a previously instantiated bean ("mainDataSource"), and set it as a property of a new bean (in this case,
we set it to the "dataSource" property of the SessionFactory.
So far, these are all things you mostly copy from one project to the next. Now, lets take a look at our only project-specific bean on this file: the SingerBo business object.
<bean id="singerBo" class="org.hibernar.business.SingerBoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
If you go back to the Java code of SingerBoImpl,java, you will see that it expected to receive a SessionFactory as a property.
It is in here where the Application Context creates a singleton SessionFactory at assings it to a singleton singerBo.
Finally, another two standard configuration pieces, that help our database operations to be wrapped into transactions as needed:
The first bean, hibernateTransactionManager, will work behind the scenes with your Hibernate session factory, to ensure that database transactions are created when needed.
<bean id="mainTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
The second little piece just instructs Spring to expect annotations (remember the @Transactional annotation in SingerBo?) on those business objects than need to generate transactions.
<tx:annotation-driven transaction-manager="mainTransactionManager"/>
That's it for the applicationContext.xml. Notice that most of the content of the applicationContext.xml, in this simple example, is "infrastructure" configuration (connection pooling, Hibernate session factoy,
delcarative transaction configuration, etc), and very little application-specific content (just our SingerBo business objects).
This example lays the groundwork for a bigger, enterprise application. As your application grows and adds more business objects,
just more instantiating xml fragments to this applicationContext.xml file.
As a matter of organization, the applicatioContext.xml file is reserved for "core" Spring features. Web-specific configuration is done in a separate Spring configuration
file, traditionally called ui-servlet.xml.
In this file we instantiate and use our Spring controllers (in our case, our simple SingerController), and redirect URL requests to the proper controller.
This is the full listing. Again, later we will proceed to explain it piece by piece
WEB-INF/ui-servlet.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="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-2.0.xsd">
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="views"/>
</bean>
<bean id="singerController" class="org.hibernar.controllers.SingerController">
<property name="singerBo" ref="singerBo"/>
</bean>
<!-- OSIV -->
<bean id="openSessionInView" class="org.springframework.orm.hibernate3.support.OpenSessionInViewFilter">
<property name="sessionFactoryBeanName" value="sessionFactory"/>
</bean>
<!-- mappings -->
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/singers.do">singerController</prop>
</props>
</property>
</bean>
</beans>
First, the xml preamble as usual (less those stylesheets we don't use for this file).
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="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-2.0.xsd">
Then, the "viewResolver". This is other of those standard parts that you will most likely copy from project to project.
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="views"/>
</bean>
In a web application as simple as ours, with only one JSP, having a viewResolver doesn't make much sense.
But as your application grows in complexity and starts adding more JSP, it is sometimes a good idea to "externalize" the name and location
of those JSP to a properties file (in our case, "views.properties"), in much the same way we externalized our logging and database connection
properties.
This is better than just hard-coding the name and location of the JSP file in your Controller's code.
WEB-INF/classes/views.properties
singers.class=org.springframework.web.servlet.view.JstlView
singers.url=/WEB-INF/views/singers.jsp
Remember the "views" string our controller returned inside the ModelAndView object? The viewResolver object will look into this "views.properties" files
to determine the exact location of the JSP file.
The following piece of our ui-servlet.xml configuration file is the instantiation and wiring of our controller, singerController.
<bean id="singerController" class="org.hibernar.controllers.SingerController">
<property name="singerBo" ref="singerBo"/>
</bean>
Notice how our controller is assigned a "singerBo" object, which is already available in the Application Context, having been defined before.
A "mappings" section lists which controller will receive the contro, according to the URL of the request.
Typically, but not necessarily, applications that use Spring-MVC use URLs ending with ".do".
In our case there is only one controller, and we will use only one URL. But as you application incorporates more URLs and controllers,
you would just add them to the mappings list.
<!-- mappings -->
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/singers.do">singerController</prop>
</props>
</property>
</bean>
Finally, another standard (although very important) general configuration piece: the OSIV filter.
OSIV stands for "Open Session in View", which is the technique used by Spring to manage Hibernate sessions.
That's it, no more configuration files.
The last thing you need to do, is to modify your web.xml so that it knows that Spring has to be activated.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"file:/opt/nes-6.1/bin/https/dtds/web-app_2_3.dtd" >
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml,/WEB-INF/ui-servlet.xml
</param-value>
</context-param>
<filter>
<filter-name>openSessionInView</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>openSessionInView</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>ui</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ui</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
Our modifications consist of:
- adding the location of our 2 Spring configuration files: applicationContext.xml and ui-servlet.xml
- adding a listener that will actually load our application context
- map all URLs ending with "*.do" to a servlet called ui, which will refer to the Controllers we created in our Application Context
Libraries
Between their jars and their supporting jars, Spring + Hibernate require an impressive list of libraries (as of Spring 2.2.5 and Hibernate 3.2.2)
Here is a list of he libraries needed for this project, which are more or less typical of any Spring+Hibernate project.
This list is for reference only, you won't need to find these jars one by one. When downloading Spring and Hibernate, just make sure
that you download the jars "with dependencies". That will cover most, if not all, of the jars listed here.
443.432 antlr-2.7.6.jar
610.790 c3p0-0.9.1.2.jar
263.854 ehcache-1.5.0.jar
188.671 commons-beanutils-1.7.0.jar
46.725 commons-codec-1.3.jar
352.668 log4j-1.2.8.jar
193.662 spring-web-2.5.5.jar
404.466 spring-webmvc-2.5.5.jar
327.324 spring-jdbc-2.5.5.jar
287.235 spring-core-2.5.5.jar
326.477 spring-aop-2.5.5.jar
486.031 spring-beans-2.5.5.jar
473.513 spring-context-2.5.5.jar
94.147 spring-context-support-2.5.5.jar
374.339 spring-orm-2.5.5.jar
231.371 spring-tx-2.5.5.jar
168.446 commons-digester-1.6.jar
38.015 commons-logging-1.0.4.jar
189.284 concurrent-1.3.4.jar
4.467 aopalliance-1.0.jar
207.723 commons-lang-2.1.jar
571.259 commons-collections-3.2.jar
226.915 jaxen-1.1.1.jar
327.810 backport-util-concurrent-3.0.jar
20.812 jstl-1.0.6.jar
508.651 standard-1.0.6.jar
2.766.130 hibernate-3.2.2.jar
313.898 dom4j-1.6.1.jar
267.771 mx4j-jmx-2.0.1.jar
164.075 mx4j-remote-2.0.1.jar
391.272 mx4j-2.0.1.jar
485.831 mx4j-tools-2.1.1.jar
107.631 commons-dbcp-1.2.1.jar
26.687 commons-dbutils-1.0.jar
42.492 commons-pool-1.2.jar
71.442 commons-discovery-0.2.jar
61.562 commons-io-1.1.jar
282.338 cglib-2.1.3.jar
26.361 asm-1.5.3.jar
16.757 asm-attrs-1.5.3.jar
109.670 commons-modeler-1.1.jar
391.834 log4j-1.2.15.jar
20.124 slf4j-api-1.5.3.jar
9.597 slf4j-log4j12-1.5.3.jar
8.809 jta-1.1.jar
115.177 mkjdbc-1.0.2.jar
594.448 javassist-3.8.1.jar
Question/comments about this article?
Post them in my forum, where I will always try to give you a friendly (if not expert) answer.