Generic Data Access Objects in hibernate

The DAO interfaces
An implementation with Hibernate
Preparing DAOs with factories
Preparing DAOs with manual dependency injection
Preparing DAOs with lookup
Writing DAOs as managed EJB 3.0 components

This time I based the DAO example on interfaces. Tools like Hibernate already provide database portability, so persistence layer portability shouldn't be a driving motivation for interfaces. However, DAO interfaces make sense in more complex applications, when several persistence services are encapsulate in one persistence layer. I'd say that you should use Hibernate (or Java Persistence APIs) directly in most cases, the best reason to use an additional DAO layer is higher abstraction (e.g. methods like getMaximumBid() instead of session.createQuery(...) repeated a dozen times).

The DAO interfaces

I use one interface per persistent entity, with a super interface for common CRUD functionality:

public interface GenericDAO {

T findById(ID id, boolean lock);

List findAll();

List findByExample(T exampleInstance);

T makePersistent(T entity);

void makeTransient(T entity);
}

You can already see that this is going to be a pattern for a state-oriented data access API, with methods such as makePersistent() and makeTransient(). Furthermore, to implement a DAO you have to provide a type and an identifier argument. As for most ORM solutions, identifier types have to be serializable.

The DAO interface for a particular entity extends the generic interface and provides the type arguments:

public interface ItemDAO extends GenericDAO {

public static final String QUERY_MAXBID = "ItemDAO.QUERY_MAXBID";
public static final String QUERY_MINBID = "ItemDAO.QUERY_MINBID";

Bid getMaxBid(Long itemId);
Bid getMinBid(Long itemId);

}

We basically separate generic CRUD operations and actual business-related data access operations from each other. (Ignore the named query constants for now, they are convenient if you use annotations.) However, even if only CRUD operations are needed for a particular entity, you should still write an interface for it, even it it is going to be empty. It is important to use a concrete DAO in your controller code, otherwise you will face some refactoring once you have to introduce specific data access operations for this entity.

An implementation with Hibernate

An implementation of the interfaces could be done with any state-management capable persistence service. First, the generic CRUD implementation with Hibernate:

public abstract class GenericHibernateDAO
implements GenericDAO {

private Class persistentClass;
private Session session;

public GenericHibernateDAO() {
this.persistentClass = (Class) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}

@SuppressWarnings("unchecked")
public void setSession(Session s) {
this.session = s;
}

protected Session getSession() {
if (session == null)
throw new IllegalStateException("Session has not been set on DAO before usage");
return session;
}

public Class getPersistentClass() {
return persistentClass;
}

@SuppressWarnings("unchecked")
public T findById(ID id, boolean lock) {
T entity;
if (lock)
entity = (T) getSession().load(getPersistentClass(), id, LockMode.UPGRADE);
else
entity = (T) getSession().load(getPersistentClass(), id);

return entity;
}

@SuppressWarnings("unchecked")
public List findAll() {
return findByCriteria();
}

@SuppressWarnings("unchecked")
public List findByExample(T exampleInstance, String[] excludeProperty) {
Criteria crit = getSession().createCriteria(getPersistentClass());
Example example = Example.create(exampleInstance);
for (String exclude : excludeProperty) {
example.excludeProperty(exclude);
}
crit.add(example);
return crit.list();
}

@SuppressWarnings("unchecked")
public T makePersistent(T entity) {
getSession().saveOrUpdate(entity);
return entity;
}

public void makeTransient(T entity) {
getSession().delete(entity);
}

public void flush() {
getSession().flush();
}

public void clear() {
getSession().clear();
}

/**
* Use this inside subclasses as a convenience method.
*/
@SuppressWarnings("unchecked")
protected List findByCriteria(Criterion... criterion) {
Criteria crit = getSession().createCriteria(getPersistentClass());
for (Criterion c : criterion) {
crit.add(c);
}
return crit.list();
}

}

There are some interesting things in this implementation. First, it clearly needs a Session to work, provided with setter injection. You could also use constructor injection. How you set the Session and what scope this Session has is of no concern to the actual DAO implementation. A DAO should not control transactions or the Session scope.

We need to suppress a few compile-time warnings about unchecked casts, because Hibernate's interfaces are JDK 1.4 only. What follows are the implementations of the generic CRUD operations, quite straightforward. The last method is quite nice, using another JDK 5.0 feature, varargs. It helps us to build Criteria queries in concrete entity DAOs. This is an example of a concrete DAO that extends the generic DAO implementation for Hibernate:

public class ItemDAOHibernate
extends GenericHibernateDAO
implements ItemDAO {

public Bid getMaxBid(Long itemId) {
Query q = getSession().getNamedQuery(ItemDAO.QUERY_MAXBID);
q.setParameter("itemid", itemId);
return (Bid) q.uniqueResult();
}

public Bid getMinBid(Long itemId) {
Query q = getSession().getNamedQuery(ItemDAO.QUERY_MINBID);
q.setParameter("itemid", itemId);
return (Bid) q.uniqueResult();
}

}

Another example which uses the findByCriteria() method of the superclass with variable arguments:

public class CategoryDAOHibernate
extends GenericHibernateDAO
implements CategoryDAO {

public Collection findAll(boolean onlyRootCategories) {
if (onlyRootCategories)
return findByCriteria( Expression.isNull("parent") );
else
return findAll();
}
}

Preparing DAOs with factories

We could bring it all together in a DAO factory, which not only sets the Session when a DAO is constructed but also contains nested classes to implement CRUD-only DAOs with no business-related operations:

public class HibernateDAOFactory extends DAOFactory {

public ItemDAO getItemDAO() {
return (ItemDAO)instantiateDAO(ItemDAOHibernate.class);
}

public CategoryDAO getCategoryDAO() {
return (CategoryDAO)instantiateDAO(CategoryDAOHibernate.class);
}

public CommentDAO getCommentDAO() {
return (CommentDAO)instantiateDAO(CommentDAOHibernate.class);
}

public ShipmentDAO getShipmentDAO() {
return (ShipmentDAO)instantiateDAO(ShipmentDAOHibernate.class);
}

private GenericHibernateDAO instantiateDAO(Class daoClass) {
try {
GenericHibernateDAO dao = (GenericHibernateDAO)daoClass.newInstance();
dao.setSession(getCurrentSession());
return dao;
} catch (Exception ex) {
throw new RuntimeException("Can not instantiate DAO: " + daoClass, ex);
}
}

// You could override this if you don't want HibernateUtil for lookup
protected Session getCurrentSession() {
return HibernateUtil.getSessionFactory().getCurrentSession();
}

// Inline concrete DAO implementations with no business-related data access methods.
// If we use public static nested classes, we can centralize all of them in one source file.

public static class CommentDAOHibernate
extends GenericHibernateDAO
implements CommentDAO {}

public static class ShipmentDAOHibernate
extends GenericHibernateDAO
implements ShipmentDAO {}

}

This concrete factory for Hibernate DAOs extends the abstract factory, which is the interface we'll use in application code:

public abstract class DAOFactory {

/**
* Creates a standalone DAOFactory that returns unmanaged DAO
* beans for use in any environment Hibernate has been configured
* for. Uses HibernateUtil/SessionFactory and Hibernate context
* propagation (CurrentSessionContext), thread-bound or transaction-bound,
* and transaction scoped.
*/
public static final Class HIBERNATE = org.hibernate.ce.auction.dao.hibernate.HibernateDAOFactory.class;

/**
* Factory method for instantiation of concrete factories.
*/
public static DAOFactory instance(Class factory) {
try {
return (DAOFactory)factory.newInstance();
} catch (Exception ex) {
throw new RuntimeException("Couldn't create DAOFactory: " + factory);
}
}

// Add your DAO interfaces here
public abstract ItemDAO getItemDAO();
public abstract CategoryDAO getCategoryDAO();
public abstract CommentDAO getCommentDAO();
public abstract ShipmentDAO getShipmentDAO();

}

Note that this factory example is suitable for persistence layers which are primarily implemented with a single persistence service, such as Hibernate or EJB 3.0 persistence. If you have to mix persistence APIs, for example, Hibernate and plain JDBC, the pattern changes slightly. Keep in mind that you can also call session.connection() inside a Hibernate-specific DAO, or use one of the many bulk operation/SQL support options in Hibernate 3.1 to avoid plain JDBC.

Finally, this is how data access now looks like in controller/command handler code (pick whatever transaction demarcation strategy you like, the DAO code doesn't change):

// EJB3 CMT: @TransactionAttribute(TransactionAttributeType.REQUIRED)
public void execute() {

// JTA: UserTransaction utx = jndiContext.lookup("UserTransaction");
// JTA: utx.begin();

// Plain JDBC: HibernateUtil.getCurrentSession().beginTransaction();

DAOFactory factory = DAOFactory.instance(DAOFactory.HIBERNATE);
ItemDAO itemDAO = factory.getItemDAO();
UserDAO userDAO = factory.getUserDAO();

Bid currentMaxBid = itemDAO.getMaxBid(itemId);
Bid currentMinBid = itemDAO.getMinBid(itemId);

Item item = itemDAO.findById(itemId, true);

newBid = item.placeBid(userDAO.findById(userId, false),
bidAmount,
currentMaxBid,
currentMinBid);

// JTA: utx.commit(); // Don't forget exception handling

// Plain JDBC: HibernateUtil.getCurrentSession().getTransaction().commit(); // Don't forget exception handling

}

The database transaction, either JTA or direct JDBC, is started and committed in an interceptor that runs for every execute(), following the Open Session in View pattern. You can use AOP for this or any kind of interceptor that can be wrapped around a method call, see Session handling with AOP.

Preparing DAOs with manual dependency injection

You don't need to write the factories. You can as well just do this:

// EJB3 CMT: @TransactionAttribute(TransactionAttributeType.REQUIRED)
public void execute() {

// JTA: UserTransaction utx = jndiContext.lookup("UserTransaction");
// JTA: utx.begin();

// Plain JDBC: HibernateUtil.getCurrentSession().beginTransaction();

ItemDAOHibernate itemDAO = new ItemDAOHibernate();
itemDAO.setSession(HibernateUtil.getSessionFactory().getCurrentSession());

UserDAOHibernate userDAO = new UserDAOHibernate();
userDAO.setSession(HibernateUtil.getSessionFactory().getCurrentSession());

Bid currentMaxBid = itemDAO.getMaxBid(itemId);
Bid currentMinBid = itemDAO.getMinBid(itemId);

Item item = itemDAO.findById(itemId, true);

newBid = item.placeBid(userDAO.findById(userId, false),
bidAmount,
currentMaxBid,
currentMinBid);

// JTA: utx.commit(); // Don't forget exception handling

// Plain JDBC: HibernateUtil.getCurrentSession().getTransaction().commit(); // Don't forget exception handling

}

The disadvantage here is that the implementation classes (i.e. ItemDAOHibernate and UserDAOHibernate) of the persistence layer are exposed to the client, the controller. Also, constructor injection of the current Session might be more appropriate.

Preparing DAOs with lookup

Alternatively, call HibernateUtil.getSessionFactory().getCurrentSession() as a fallback, if the client didn't provide a Session when the DAO was constructed:

public abstract class GenericHibernateDAO
implements GenericDAO {

private Class persistentClass;
private Session session;

public GenericHibernateDAO() {
this.persistentClass = (Class) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}

public void setSession(Session session) {
this.session = session;
}

protected void getSession() {
if (session == null)
session = HibernateUtil.getSessionFactory().getCurrentSession();
return session;
}

...

The controller now uses these stateless data access objects through direct instantiation:

// EJB3 CMT: @TransactionAttribute(TransactionAttributeType.REQUIRED)
public void execute() {

// JTA: UserTransaction utx = jndiContext.lookup("UserTransaction");
// JTA: utx.begin();

// Plain JDBC: HibernateUtil.getCurrentSession().beginTransaction();

ItemDAO itemDAO = new ItemDAOHibernate();
UserDAO userDAO = new UserDAOHibernate();

Bid currentMaxBid = itemDAO.getMaxBid(itemId);
Bid currentMinBid = itemDAO.getMinBid(itemId);

Item item = itemDAO.findById(itemId, true);

newBid = item.placeBid(userDAO.findById(userId, false),
bidAmount,
currentMaxBid,
currentMinBid);

// JTA: utx.commit(); // Don't forget exception handling

// Plain JDBC: HibernateUtil.getCurrentSession().getTransaction().commit(); // Don't forget exception handling

}

The only disadvantage of this very simple strategy is that the implementation classes (i.e. ItemDAOHibernateUserDAOHibernate) of the persistence layer are again exposed to the client, the controller. You can still supply a custom Session if needed (integration test, etc). and

Each of these methods (factories, manual injection, lookup) for setting the current Session and creating a DAO instance has advantages and drawbacks, use whatever you feel most comfortable with.

Naturally, the cleanest way is managed components and EJB 3.0 session beans:

Writing DAOs as managed EJB 3.0 components

Turn your DAO superclass into a base class for stateless session beans (all your concrete DAOs are then stateless EJBs, they already have a business interface). This is basically a single annotation which you could even move into an XML deployment descriptor if you like. You can then use dependency injection and get the "current" persistence context provided by the container:

@Stateless
public abstract class GenericHibernateDAO
implements GenericDAO {

private Class persistentClass;

@PersistenceContext
private EntityManager em;

public GenericHibernateDAO() {
setSession( (Session)em.getDelegate() );
}

...

You can then cast the delegate of an EntityManager to a Hibernate Session.

This only works if you use Hibernate as a Java Persistence provider, because the delegate is the SessionSession injected directly. If you use a different Java Persistence provider, rely on the EntityManager API instead of Session. Now wire your DAOs into the controller, which is also a managed component: API. In JBoss AS you could even get a

@Stateless
public class ManageAuctionController implements ManageAuction {

@EJB ItemDAO itemDAO;
@EJB UserDAO userDAO;

@TransactionAttribute(TransactionAttributeType.REQUIRED) // This is even the default
public void execute() {

Bid currentMaxBid = itemDAO.getMaxBid(itemId);
Bid currentMinBid = itemDAO.getMinBid(itemId);

Item item = itemDAO.findById(itemId, true);

newBid = item.placeBid(userDAO.findById(userId, false),
bidAmount,
currentMaxBid,
currentMinBid);

}
}

How JAAS enables use of custom security repositories with J2EE applications

This tutorial describes how a developer can write a custom JAASTM LoginModule for using an LDAP authentication data store along with a JavaTM 2 Platform, Enterprise Edition (J2EETM) application.

The tutorial includes a sample implementation of a LDAP based LoginModule which is downloadable as jaastutorial.zip. The archive contains:

  1. jaastutorial.jar
  2. jaasclient.jar
  3. jaastutorial.bat
  4. jaastutorial.sh
  5. login.cfg
The following products were used in creating this tutorial:
  1. Pramati Server 3.0
  2. Pramati Studio 3.0
  3. OpenLDAP 2.0.18
The following specifications apply to this tutorial:
  1. Java Authentication and Authorization ServiceTM 1.0
  2. JavaTM 2 Platform, Enterprise Edition 1.3
  3. Lightweight Directory Access Protocol 3.0

Why JAAS in J2EE
J2EE model: roles, users and JAAS
Writing the JAAS security module
Step 1: Writing the LoginModule
initialize() method
login() method
commit() method
abort() method
logout() method
Exceptions thrown by LoginModule methods
Step 2: Writing the CallBackHandler
Step 3: Configuring the J2EE application
Step 4: Packaging the LoginModule along with application
Step 5: Integrating the LoginModule with the J2EE server
Sample LDAP-based LoginModule
Installing and configuring the OpenLDAP Server
LDAP parameters for LoginModule and User Manager
Configuring LoginModule with Pramati Server
Configuring the User Manager
Creating users and roles from the User Manager
Setting the client classpath
Running the sample
Summary


Why JAAS in J2EE

One of the limitations of J2EE Version 1.2 platform was it did not provide application developers with a standard route to integrating the application server realm with existing or custom security infrastructures. J2EE Version 1.3 now solves that with the inclusion of Java Authentication and Authorization Service (JAAS™) framework.

J2EE application servers that implement JAAS provide enterprise application developers with the standard Login Module API for tapping custom or legacy security systems from their applications. While application developers write to the LoginModule API (specifically, LoginContext API), the application server implements the LoginModule interface.

The standards-based LoginModule interface gives J2EE developers the freedom to tap a variety of information sources that use Java Database Connectivity, the lightweight directory access protocol (LDAP) or Shared File Systems to store authentication data - without requiring them to modify the application code.

Indeed, there are increasing number of scenarios where J2EE application developers wish to tap custom authentication repositories from their applications. They would do this by writing a Login Module, packaging it along with their application and distributing to target J2EE application servers in a prescribed way.

J2EE Model: Roles, Users and JAAS

The J2EE model defines security at two levels: system and application. System level security is defined in terms of User Groups, called Roles, and in terms of security privileges mapping definitions, called Realms. Realms are mappings of one or more User Groups to a set of privileges or permissions.

Application level security is constituted from User Groups and Realms. At the application level, security permissions also list the various application components that are accessible by each User Group in each Realm. Thus, when an application is deployed, its application level realms and roles are mapped to the system level realms and roles defined on the server.

J2EE application servers implementing JAAS enable application developers to write a custom "pluggable" login module in the server environment. Such a module provides a conduit for roles defined in the packaged application to user group information stored in some custom authentication repository, say an LDAP server.


How LoginModule helps application roles and groups map to authentication data stored in a custom repository such as LDAP.
The Role of the LoginModule in J2EE Security Model



Writing the JAAS Security Module

A J2EE application developer writing security with JAAS would basically write the Login Module. The JAAS interface implementation holds the authentication logic. Application servers typically ship with standard Login Module implementations. Application developers may want to write their own implementation, and will see how in this tutorial in the following steps:

  1. Writing the LoginModule interface (LoginContext API)
  2. Writing the CallBackHandler interface that enables client to pass authentication data to the server
  3. Configuring the LoginModule and CallBackHandler with the server and application
  4. Packaging the application along with module classes
  5. Integrating the LoginModule with the application server


Step 1: Writing the LoginModule

In this tutorial, you will see code snippets from a LoginModule implementation for an LDAP Server. We also demonstrate how to test the LDAP LoginModule sample in a typical J2EE application server environment.

This is how the LoginModule implementation class is defined:

 public class LDAPLoginModule implements LoginModule

The standard JAAS packages required by this class are imported as shown here:

 import javax.security.*;
Standards methods in the LoginModule that must be implemented are:
  1. initialize()
  2. login()
  3. commit()
  4. abort()
  5. logout()

initialize()

The initialize method does the following:
  1. Sets configurations required by the LoginModule
  2. Collects login information that is encapsulated in the CallBackHandler
  3. Initializes and instantiates all configuration parameters for this instance of the LoginModule

The client instantiates the LoginContext object and passes a CallBackHandler instance with the user name and password. When the LoginContext object is instantiated, the initialize() method of the LoginModule is triggered.

  public static void main(String args[])
{
LoginContext lc = new LoginContext("Login",
new MyCallbackHandler(args[0],args[1]));
}

login()

This method returns a boolean variable, which is true if the authentication information provided is valid. The login method performs the following tasks:
  1. Fetches the login information
  2. Authenticates the user

The login information is fetched using the CallBackHandler. The code that does this is shown here:

Callback[] calls=new Callback[2];
calls[0]=new NameCallback("name");
calls[1]=new PasswordCallback("Password",false);
callbackHandler.handle(calls);

The login method tries to connect to the server using the login information that is fetched. If the connection is established, the method returns the value true. The following code snippet shows this:

  boolean verification=false;
…try{
props.put(Context.SECURITY_PRINCIPAL,cbUserName);
props.put(Context.SECURITY_CREDENTIALS,cbPassword);
ctx = new InitialDirContext(props);
verification=true;
}…
return verification;

This code changes with the actual type of security framework for which the LoginModule is written.

commit() method

This method sets the subject in the session to the username that is validated by the login method. It also populates the subject with roles specified in the LDAP server (in this tutorial) for that user, and returns true. If the user is not validated, the commit method returns false. The following code snippet shows this:

  if(verification)
{subject.getPrincipals().add(userName);
…subject.getPrincipals().add(role);
return true;
}else return false;

abort() method

This method is used to exit the LoginModule in case of runtime exceptions and is usually triggered by the application server. This method is invoked after the abort() method of LoginContext. The application developer must not directly call the abort method of the LoginContext interface.

logout() method

This method clears the principal settings of the subject in the session. It removes the privilege settings associated with the roles of the subject. The following code snippet shows this:
  subject.getPrincipals().clear();
verification=false;
return true;

Exceptions thrown by LoginModule methods

According to the JAAS specifications, all LoginModule methods should only throw a LoginException. Any other exception during LoginModule execution should be caught and a LoginException thrown against it. The following code snippet shows how this can be done:

   public boolean login() throws LoginException
{

catch(IOException e)
{throw new LoginException(e.toString());}
…}

Step 2: Writing the CallBackHandler

The CallBackHandler is the JAAS interface that defines the type of data used for authentication. For example, a username-password or a user-certificate combination forms a security identity and credential pair. The type of data used for validating the identity is defined as part of the implementation of the CallBackHandler interface.

The CallbackHandler implementation contains a single method, handle(). The following code snippet from the CallBackHandler distributed with the sample client application ClientLoginSample.java demonstrates this:

  static class MyCallbackHandler implements CallbackHandler
{
private String username;
private String password;

The handle() method sets the value of the username and password attributes, passed by the client application, in the LoginModule's CallBackHandler. The following code snippet shows this:

  handle(){

if(callbacks[i] instanceof NameCallback){
NameCallback ncb = (NameCallback)callbacks[i];
ncb.setName(username);}
if(callbacks[i] instanceof PasswordCallback){
PasswordCallback pcb = (PasswordCallback)callbacks[i];
pcb.setPassword(password.toCharArray());
}}

Step 3: Configuring the J2EE application

A J2EE application package includes descriptors that contain information about security privileges for various modules/components of the application. Security privilege is defined at the application level and is associated with realms and roles. During deployment, these roles are mapped to roles defined in the server-level realm.

Configuring a login module with a J2EE application involves the following steps:

  1. Creating a login UI that validates user information by calling the LoginContext interface.
  2. Creating application level realms and roles, and mapping permissions to application components
  3. Distributing the Login Module classes with the application

Step 4: Packaging the Login Module along with Application

A J2EE application developer would want to configure the LoginModule with a target J2EE application server. Therefore, the LoginModule, along with the helper classes, is packaged into a separate JAR file that may be distributed independent of the application archives, and separately loaded from the server classpath.

For this reason, there must be no code-level dependencies in the LoginModule on the J2EE application.

Step 5: Integrating the Login Module with the J2EE Application Server

The deployment process for a LoginModule is unlike J2EE applications and involves configuring the target application server.

A J2EE application server organizes its security environment into realms, each of which maps to one or more login modules, and has one or more users and roles defined on it. This differs from application realms, which are associated with security permissions for the application and do not assume the existence of any LoginModule.

Integrating a LoginModule with the application server involves the following steps:

  1. Configuring the server with a realm that uses a specific LoginModule for security authentication.
  2. Mapping the application realm and roles to the realm and roles defined by the LoginModule.

Sample LDAP-based LoginModule

The tutorial includes a sample implementation of a LDAP based LoginModule. Download the required sample files here (jaastutorial.zip).

We illustrate the steps to integrate this implementation of the LDAP-based LoginModule with Pramati Server using a sample application.

The sample application uses Pramati's implementation of the User Manager Module, which creates users on the LDAP Server and maps roles to various user names defined on the LDAP server. Get the User Manager API here.

Installing and configuring the LDAP server

This tutorial is written assuming a particular directory structure on a LDAP Server.

If you do not have a LDAP Server installed, download one from www.openldap.org. You may access this FTP site. OpenLDAP Server is distributed for Unix/Linux platforms.

Download the Quickstart Guide for LDAP and do as follows to create the required directory structure:

  1. Replace the directory names MY-DOMAIN and example with the name my_company.
  2. In the LDAP server configuration file slapd.conf, set password against the name rootpw as my_password.
  3. Disable the schema checks on the LDAP server by adding the following line to the slapd.conf file: schemacheck off
  4. After configuring the LDAP Server with the directory structure, start the server as instructed in the Quickstart Guide.


LDAP parameters for Login Module and the User Manager


NameValue

LDAPServerUrl The URL and port of the LDAP Server. If the LDAP server is running on the same host as the application server, it would be ldap://localhost:389 (with LDAP Server on port 389)

LDAPContextFactory A default standard context factory implementation is provided by Sun. Set this field ONLY if you are using an other Context Factory. Add the factory to the server classpath too. You will need to restart the server.

Realmname This is the application server-level realm. For example, ldap

LDAPUserKey cn

LDAPUserSuperContext For example, dc=my_company, dc=com

LDAPRolesKey sn

LDAPSuperUserDN For example, cn=manager,dc=my_company,dc=com. This refers to the distinguishing name of the LDAP super user and is set by the LDAP administrator.

LDAPSuperUserPassword The LDAP super user password to the server. This is needed to allow addition and removal of users by the LDAP User Manager.

Configuring LoginModule with Pramati Server

  1. Place the jaastutorial.jar in the classpath of the server by editing the setup.bat.
  2. Run setup.bat from the command line.
  3. Start the Server and then start the Console.
  4. Select the Security node in the tree panel on the Console and select '+' for adding an LDAP realm.
  5. In the View Panel, choose an appropriate server realm name, say, ldap.
  6. Add the path to the LoginModule class as com.pramati.security.loginmodules.ldap.LDAPLoginModule.
  7. Specify the Flag as Required.
  8. The Init options require multiple name-value pairs and these are listed as LDAP parameters in the section above.

Configuring the User Manager

  1. On the same page, give the fully qualified path of the User Manager class as com.pramati.security.loginmodules.ldap.LDAPUserManager
  2. The User Manager needs the same name-value pairs as the LoginModule in the Init Options.

After configuring the LoginModule and the UserManager for the LDAP realm, click on Add button to register the LDAP realm with the application server.

Ensure that the jaastutorial.jar package is present in the server classpath, before starting the server.

Creating users and roles using the User Manager

From the Console, configuring new users with unique user names and passwords, roles within the realm, and mapping the roles and users effectively test the LDAP User Manager Module. If the configuration is correct, new users and their role mappings are added to the realm that can be viewed from the Console.

Setting the client classpath

The ClientLoginSample is a sample Java client that connects to the LDAP server through Pramati Server. The client classpath is modified to point to sample classes and files, as well as the server classes required to run the Java client.

The ClientLoginSample and helper classes are packaged in jaasclient.jar. This must be placed in the client classpath.

The client requires the location of the Pramati Server and the realm to log in to. This information is stored in a file, login.cfg, which is packaged along with this tutorial. This file must be placed in the client classpath.

Edit the login.cfg file to point to the server location and realm name. For example,

  com.pramati.realm="realm://127.0.0.1:9191/ldap"

The client also requires the following application server packages in its classpath:
jaas.jar
jta.jar
pramati_client.jar
security_interop.jar

Edit the file jaastutorial.bat to point to the installation directory of Pramati Server. Run jaastutorial.bat from the command line to set the client classpath.

Running the sample

After setting the client classpath, run the ClientLoginSample program by executing:

  java -Djava.security.auth.login.config=
com.pramati.security.samples.ClientLoginSample
Supply the username and password that has been configured on the LDAP server. The sample program will return the Role to which the User has been mapped. It then logs out the user from the realm. An error message is displayed if the username-password submitted is invalid.

Summary

The tutorial demonstrated the simplicity with which JAAS provides standards based pluggable security. We saw how a custom LoginModule is written and integrated with a typical standards compliant J2EE application server.

Spring Hibernate

Account.java
.....................

package springexample.hibernate;

import java.math.BigDecimal;
import java.util.Date;

public class Account {

private Long id = new Long(-1);

private String accountName;

private String type;

private Double balance;

private Date createDate;

private Date updateDate;

private Customer customer;

public String getAccountName() {
return accountName;
}


public void setAccountName(String accountName) {
this.accountName = accountName;
}



public Date getCreateDate() {
return createDate;
}


public void setCreateDate(Date createDate) {
this.createDate = createDate;
}


public Long getId() {
return id;
}


public void setId(Long id) {
this.id = id;
}


public String getType() {
return type;
}


public void setType(String type) {
this.type = type;
}


public Date getUpdateDate() {
return updateDate;
}


public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}


public Customer getCustomer() {
return customer;
}



public void setCustomer(Customer customer) {
this.customer = customer;
}

public Double getBalance() {
return balance;
}

public void setBalance(Double balance) {
this.balance = balance;
}

}



AccountDAO.java
.............................
package springexample.hibernate;

import java.util.List;


public interface AccountDAO {

public abstract void addAccount(Account account);
}

AccountDAOImpl.java
...................................

package springexample.hibernate;

import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Query;
import net.sf.hibernate.Session;

import org.springframework.orm.hibernate.HibernateCallback;
import org.springframework.orm.hibernate.support.HibernateDaoSupport;


public class AccountDAOImpl extends HibernateDaoSupport implements AccountDAO{

public void addAccount(Account account) {
getHibernateTemplate().save(account);
// TODO Auto-generated method stub

}

}


customer.java
................
package springexample.hibernate;

import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Set;

public class Customer {

private Long id = new Long(-1);
private String userId;
private String password;
private String email;
private String firstName;
private String lastName;
private BigDecimal balance;

private Set accounts = new HashSet();

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}



public Long getId() {
return id;
}


public void setId(Long id) {
this.id = id;
}

public Set getAccounts() {
return accounts;
}


public void setAccounts(Set accounts) {
this.accounts = accounts;
}

public void addAccount(Account account){
account.setCustomer(this);
accounts.add(account);
}

public BigDecimal getBalance() {
return balance;
}


public void setBalance(BigDecimal balance) {
this.balance = balance;
}


public String getFirstName() {
return firstName;
}


public void setFirstName(String firstName) {
this.firstName = firstName;
}


public String getLastName() {
return lastName;
}


public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getUserId() {
return userId;
}


public void setUserId(String userId) {
this.userId = userId;
}


}

CustomerDAO.java
.................

package springexample.hibernate;

import java.util.List;

public interface CustomerDAO {

public abstract void addCustomer(Customer customer);

public abstract Customer getCustomerAccountInfo(Customer customer);
}
 
CustomerDAOImpl.java
.................
package springexample.hibernate;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.sf.hibernate.Hibernate;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Query;
import net.sf.hibernate.Session;
import org.springframework.orm.hibernate.HibernateCallback;
import org.springframework.orm.hibernate.support.HibernateDaoSupport;
public class CustomerDAOImpl extends HibernateDaoSupport implements CustomerDAO{
public void addCustomer(Customer customer) { getHibernateTemplate().save(customer); // TODO Auto-generated method stub
}
public Customer getCustomerAccountInfo(Customer customer) { Customer cust = null;
List list = getHibernateTemplate().find("from Customer customer " +"where customer.userId = ?" , customer.getUserId(),Hibernate.STRING);
if(list.size() > 0){
cust = (Customer) list.get(0);
}
return cust;
}
}

CreateBankCustomerClient.java
..............................
package springexample.hibernate;
import java.util.ArrayList;
import java.util.Date;
import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.orm.hibernate.LocalSessionFactoryBean;
public class CreateBankCustomerClient {
public static ClassPathXmlApplicationContext appContext = null;
public static void main(String[] args){
try {
System.out.println("CreateBankCustomerClient started"); ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { "springexample-hibernate.xml" }); System.out.println("Classpath loaded");
Customer customer = new Customer(); customer.setEmail("anil.goud@cellarch.com.com"); customer.setUserId("anilkumar"); customer.setPassword("xxxxx");
customer.setFirstName("anil");
customer.setLastName("goud");
Account acc = new Account();
acc.setAccountName("Checking Account-Raj Malhotra"); acc.setType("C");
acc.setCreateDate(new Date());
acc.setUpdateDate(new Date());
acc.setBalance(new Double(500.00));
customer.addAccount(acc);
CustomerDAOImpl customerDAOImpl = (CustomerDAOImpl) appContext.getBean("customerDAOTarget"); customerDAOImpl.addCustomer(customer);
Customer customerRecord = customerDAOImpl.getCustomerAccountInfo(customer);
System.out.println("Customer Found , User Id is " + customerRecord.getUserId());

System.out.println("CreateBankCustomerClient end");
} catch(Exception e){
e.printStackTrace();
}
}
private static void createDatabaseSchema() throws Exception { LocalSessionFactoryBean sessionFactory = (LocalSessionFactoryBean) appContext.getBean("&frameworkSessionFactory"); sessionFactory.dropDatabaseSchema(); sessionFactory.createDatabaseSchema();
}
}


Account.hbm.xml
................

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"> <hibernate-mapping>
<class name="springexample.hibernate.Account" table="TBL_ACCOUNT" dynamic-update="false" dynamic-insert="false">
<id name="id" column="ACCOUNT_ID"
type="java.lang.Long" unsaved-value="-1" >
<generator class="native">
</generator> </id>
<many-to-one name="customer"
column="CUSTOMER_ID" class="springexample.hibernate.Customer"
not-null="true"/>
<property name="accountName" type="string"
update="false" insert="true" column="ACCOUNT_NAME"
length="50" not-null="true" />
<property name="type" type="string" update="false" insert="true" column="ACCOUNT_TYPE" length="1" not-null="true" />
<property name="createDate" type="date"
update="false" insert="true" column="CREATE_DATE" not-null="true" />
<property name="updateDate" type="date" update="true" insert="true" not-null="true" column="UPDATE_DATE" /> <property name="balance" type="double" update="true" insert="true"
column="ACCOUNT_BALANCE" not-null="true"/> </class>
</hibernate-mapping>


Customer.hbm.xml
......................


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"> <hibernate-mapping>
<class name="springexample.hibernate.Customer" table="TBL_CUSTOMER" dynamic-update="false" dynamic-insert="false"> <id name="id" column="CUSTOMER_ID" type="java.lang.Long" unsaved-value="-1" > <generator class="native"> </generator> </id> <set name ="accounts" inverse = "true" cascade="all-delete-orphan"> <key column ="CUSTOMER_ID"/> <one-to-many class="springexample.hibernate.Account"/> </set> <property name="email" type="string" update="false" insert="true" column="CUSTOMER_EMAIL" length="82" not-null="true" /> <property name="password" type="string" update="false" insert="true" column="CUSTOMER_PASSWORD" length="10" not-null="true" /> <property name="userId" type="string" update="false" insert="true" column="CUSTOMER_USERID" length="12" not-null="true" unique="true" /> <property name="firstName" type="string" update="false" insert="true" column="CUSTOMER_FIRSTNAME" length="25" not-null="true" /> <property name="lastName" type="string" update="false" insert="true" column="CUSTOMER_LASTTNAME" length="25" not-null="true" /> </class> </hibernate-mapping>


springexample-hibernate.xml
.............................


<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <!-- - Application context definition for Express on Hibernate. --> <beans> <bean id="exampleDataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName"><value>org.apache.derby.jdbc.EmbeddedDriver
</value></property>
<property name="url"><value>jdbc:derby:springexample;create=true</value></property> </bean> <bean id="exampleHibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="properties"> <props> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.dialect">net.sf.hibernate.dialect.DerbyDialect</prop> <prop key="hibernate.query.substitutions">true 'T', false 'F'</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.c3p0.minPoolSize">5</prop> <prop key="hibernate.c3p0.maxPoolSize">20</prop> <prop key="hibernate.c3p0.timeout">600</prop> <prop key="hibernate.c3p0.max_statement">50</prop> <prop key="hibernate.c3p0.testConnectionOnCheckout">false</prop> </props> </property> </bean> <!-- Hibernate SessionFactory --> <bean id="exampleSessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> <property name="dataSource"><ref local="exampleDataSource"/></property> <property name="hibernateProperties"> <ref bean="exampleHibernateProperties" /> </property> <!-- Must references all OR mapping files. --> <property name="mappingResources"> <list> <value>Customer.hbm.xml</value> <value>Account.hbm.xml</value> </list> </property> </bean> <!-- Pass the session factory to our UserDAO --> <bean id="customerDAOTarget" class="springexample.hibernate.CustomerDAOImpl"> <property name="sessionFactory"><ref local="exampleSessionFactory"/></property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager"> <property name="sessionFactory"><ref bean="exampleSessionFactory"/></property> </bean> <bean id="userDAO" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"><ref local="transactionManager"/></property> <property name="target"><ref local="customerDAOTarget"/></property> <property name="transactionAttributes"> <props> <prop key="addCustomer">PROPAGATION_REQUIRED</prop> </props> </property> </bean> </beans>

build.xml
............

<project name="springexamples" basedir="." default="main"> <property name="src.dir" value="src"/> <property name="springconf.files" value="src/spring"/> <property name="build.dir" value="build"/> <property name="classes.dir" value="${build.dir}/classes"/> <property name="jar.dir" value="${build.dir}/jar"/> <property name="main-class" value="springexample.hibernate.CreateBankCustomerClient"/> <property name="lib.dir" value="lib"/> <property name="common-lib.dir" value ="C:\spring-framework-1.2-rc2\lib\jakarta-commons"/> <property name="ecache-lib.dir" value ="C:\spring-framework-1.2-rc2\lib\ehcache"/> <property name="dom4j-lib.dir" value ="C:\spring-framework-1.2-rc2\lib\dom4j"/> <property name="cglib-lib.dir" value ="C:\spring-framework-1.2-rc2\lib\cglib"/> <property name="hibernate-lib.dir" value ="C:\spring-framework-1.2-rc2\lib\hibe rnate"/> <property name="jta-lib.dir" value ="C:\spring-framework-1.2-rc2\lib\j2ee"/> <property name="spring-lib.dir" value ="C:\spring-framework-1.2-rc2\dist"/> <property name="ojb-lib.dir" value ="C:\spring-framework-1.2-rc2\lib\ojb"/> <property name="derby-lib.dir" value ="C:\Program Files\IBM\Cloudscape_10.0\lib"/> <path id="classpath"> <fileset dir="${lib.dir}" includes="**/*.jar"/> <fileset dir="${common-lib.dir}" includes="**/*.jar"/> <fileset dir="${ecache-lib.dir}" includes="**/*.jar"/> <fileset dir="${dom4j-lib.dir}" includes="**/*.jar"/> <fileset dir="${cglib-lib.dir}" includes="**/*.jar"/> <fileset dir="${hibernate-lib.dir}" includes="**/*.jar"/> <fileset dir="${jta-lib.dir}" includes="**/*.jar"/> <fileset dir="${spring-lib.dir}" includes="**/*.jar"/> <fileset dir="${ojb-lib.dir} " includes="**/*.jar"/> <fileset dir="${derby-lib.dir}" includes="**/*.jar"/> <path location="${springconf.files}"/> </path> <target name="clean"> <delete dir="${build.dir}"/> </target> <target name="compile"> <mkdir dir="${classes.dir}"/> <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath"/> </target> <target name="jar" depends="compile"> <mkdir dir="${jar.dir}"/> <jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}"> <manifest> <attribute name="Main-Class" value="${main-class}"/> </manifest> </jar> </target> <target name="run" depends="jar"> <java fork="true" classname="${main-class}"> <classpath> <path refid="classpath"/> <path location="${jar.dir}/${ant.project.name}.jar"/> </classpath> </java> </target> <target name="clean-build" depends="clean,jar"/> <target name="main" depends="clean,run"/> </project>







Display File List In a directory

public String[] getFileNames(String DirPath) {

String directoryName;
File directory;
String[] files;
directory = new File(DirPath);
if (directory.isDirectory() == false) {
if (directory.exists() == false) {
System.out.println("There is no such directory!");
return null;

} else {
System.out.println("That file is not a directory.");
return null;
}
} else {
files = directory.list();
// System.out.println("Files in directory -" + directory + "/:");
for (int i = 0; i < files.length; i++) {
//System.out.println(" " + files[i]);

}
return files;
}
}

Date Format in java

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class DateFormat {

static public void displayDate(Locale currentLocale) {

Date today;
String result;
SimpleDateFormat formatter;

formatter = new SimpleDateFormat("EEE d MMM yy", currentLocale);
today = new Date();
result = formatter.format(today);

System.out.println("Locale: " + currentLocale.toString());
System.out.println("Result: " + result);
}

static public void displayPattern(String pattern, Locale currentLocale) {

Date today;
SimpleDateFormat formatter;
String output;

formatter = new SimpleDateFormat(pattern, currentLocale);
today = new Date();
output = formatter.format(today);

System.out.println(pattern + " " + output);
}

static public void main(String[] args) {

Locale[] locales = { new Locale("fr", "FR"), new Locale("de", "DE"),
new Locale("en", "US") };

for (int i = 0; i < locales.length; i++) {
displayDate(locales[i]);
System.out.println();
}

String[] patterns = { "dd.MM.yy", "yyyy.MM.dd G 'at' hh:mm:ss z",
"EEE, MMM d, ''yy", "h:mm a", "H:mm", "H:mm:ss:SSS",
"K:mm a,z", "yyyy.MMMMM.dd GGG hh:mm aaa" };

for (int k = 0; k < patterns.length; k++) {
displayPattern(patterns[k], new Locale("en", "US"));
System.out.println();
}

System.out.println();
}
}

Reading and writing text files


When reading and writing text files :
  • it is almost always a good idea to use buffering (default size is 8K)
  • it is often possible to use references to abstract base classes, instead of references to specific concrete classes
  • there is always a need to pay attention to exceptions (in particular, IOException and FileNotFoundException)
The close method :
  • always needs to be called, or else resources will leak
  • will automatically flush the stream, if necessary
  • calling close on a "wrapper" stream will automatically call close on its underlying stream
  • closing a stream a second time has no consequence
Commonly used items : The FileReader and FileWriter classes always use the system's default character encoding. If this default is not appropriate (for example, when reading an XML file which specifies its own encoding), the recommended alternatives are, for example :

FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader in = new InputStreamReader(fis, "UTF-8");

FileOutputStream fos = new FileOutputStream("test.txt");
OutputStreamWriter out = new OutputStreamWriter(fos, "UTF-8");

Scanner scanner = new Scanner(file, "UTF-8");

Example 1

This example uses JDK 1.4 :

import java.io.*;

public class ReadWriteTextFile {

/**
* Fetch the entire contents of a text file, and return it in a String.
* This style of implementation does not throw Exceptions to the caller.
*
* @param aFile is a file which already exists and can be read.
*/

static public String getContents(File aFile) {
//...checks on aFile are elided
StringBuilder contents = new StringBuilder();

try {
//use buffering, reading one line at a time
//FileReader always assumes default encoding is OK!
BufferedReader input = new BufferedReader(new FileReader(aFile));
try {
String line =
null; //not declared within while loop
/*
* readLine is a bit quirky :
* it returns the content of a line MINUS the newline.
* it returns null only for the END of the stream.
* it returns an empty String if two newlines appear in a row.
*/

while (( line = input.readLine()) != null){
contents.append(line);
contents.append(System.getProperty(
"line.separator"));
}
}
finally {
input.close();
}
}
catch (IOException ex){
ex.printStackTrace();
}

return contents.toString();
}

/**
* Change the contents of text file in its entirety, overwriting any
* existing text.
*
* This style of implementation throws all exceptions to the caller.
*
* @param aFile is an existing file which can be written to.
* @throws IllegalArgumentException if param does not comply.
* @throws FileNotFoundException if the file does not exist.
* @throws IOException if problem encountered during write.
*/

static public void setContents(File aFile, String aContents)
throws FileNotFoundException, IOException {
if (aFile == null) {
throw new IllegalArgumentException("File should not be null.");
}
if (!aFile.exists()) {
throw new FileNotFoundException ("File does not exist: " + aFile);
}
if (!aFile.isFile()) {
throw new IllegalArgumentException("Should not be a directory: " + aFile);
}
if (!aFile.canWrite()) {
throw new IllegalArgumentException("File cannot be written: " + aFile);
}

//use buffering
Writer output = new BufferedWriter(new FileWriter(aFile));
try {
//FileWriter always assumes default encoding is OK!
output.write( aContents );
}
finally {
output.close();
}
}

/** Simple test harness. */
public static void main (String... aArguments) throws IOException {
File testFile =
new File("C:\\Temp\\blah.txt");
System.out.println(
"Original file contents: " + getContents(testFile));
setContents(testFile,
"The content of this file has been overwritten...");
System.out.println(
"New file contents: " + getContents(testFile));
}
}


Example 2

This example demonstrates using Scanner to read a file line by line (it does not perform a write operation) :

import java.io.*;
import java.util.Scanner;

public final class ReadWithScanner {

public static void main(String... aArgs) throws FileNotFoundException {
ReadWithScanner parser =
new ReadWithScanner("C:\\Temp\\test.txt");
parser.processLineByLine();
log(
"Done.");
}

/**
* @param aFileName full name of an existing, readable file.
*/

public ReadWithScanner(String aFileName){
fFile =
new File(aFileName);
}

/** Template method that calls {@link #processLine(String)}. */
public final void processLineByLine() throws FileNotFoundException {
Scanner scanner =
new Scanner(fFile);
try {
//first use a Scanner to get each line
while ( scanner.hasNextLine() ){
processLine( scanner.nextLine() );
}
}
finally {
//ensure the underlying stream is always closed
scanner.close();
}
}

/**
* Overridable method for processing lines in different ways.
*
*

This simple default implementation expects simple name-value pairs, separated by an
* '=' sign. Examples of valid input :
* height = 167cm
* mass = 65kg
* disposition = "grumpy"
* this is the name = this is the value
*/


protected void processLine(String aLine){
//use a second Scanner to parse the content of each line
Scanner scanner = new Scanner(aLine);
scanner.useDelimiter(
"=");
if ( scanner.hasNext() ){
String name = scanner.next();
String value = scanner.next();
log(
"Name is : " + quote(name.trim()) + ", and Value is : " + quote(value.trim()) );
}
else {
log(
"Empty or invalid line. Unable to process.");
}
//(no need for finally here, since String is source)
scanner.close();
}

// PRIVATE //
private final File fFile;

private static void log(Object aObject){
System.out.println(String.valueOf(aObject));
}

private String quote(String aText){
String QUOTE =
"'";
return QUOTE + aText + QUOTE;
}
}


Example run of this class :
Name is : 'height', and Value is : '167cm'
Name is : 'mass', and Value is : '65kg'
Name is : 'disposition', and Value is : '"grumpy"'
Name is : 'this is the name', and Value is : 'this is the value'
Done.

Spring-Hibernate

First both DAO classes implement the MyRecordDAO interface.
package com.shoesobjects;
import java.io.Serializable;
public class MyRecord implements Serializable {
private Long id = null;
private String firstName = null;
private String lastName = null;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}

Here is the data access object using just hibernate. Not to bad but lots of cookie cutter try/catch blocks and redundant opening/closing of sessions. Notice it is 114 lines of code and would be even larger if I handled exceptions properly like any good programmer would.

package com.shoesobjects.dao;
import com.shoesobjects.MyRecord;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.Configuration;
import java.util.List;
public class MyRecordDAOHibernate implements MyRecordDAO {
private static SessionFactory sessionFactory = null;
public MyRecordDAOHibernate() {
Configuration cfg =
null;
try {
cfg =
new Configuration().addClass(MyRecord.class);
}
catch (MappingException e) {
e.printStackTrace();
}
try {
sessionFactory = cfg.buildSessionFactory();
}
catch (HibernateException e) {
e.printStackTrace();
}
}
private Session getSession() {
Session session =
null;
try {
session =
sessionFactory.openSession();
}
catch (HibernateException e) {
e.printStackTrace();
}
return session;
}
public MyRecord getRecord(Long id) {
Session session =
this.getSession();
MyRecord record =
null;
try {
record = (MyRecord) session.load(MyRecord.
class, id);
}
catch (HibernateException e) {
e.printStackTrace();
}
finally {
if (session != null) {
try {
session.close();
}
catch (HibernateException e) {
e.printStackTrace();
}
}
}
return record;
}
public List getRecords() {
Session session =
this.getSession();
List list =
null;
try {
Query query = session.
createQuery(
"select myrecord from com.shoesobjects.MyRecord");
list = query.list();
}
catch (HibernateException e) {
e.printStackTrace();
}
finally {
if (session != null) {
try {
session.close();
}
catch (HibernateException e) {
e.printStackTrace();
}
}
}
return list;
}
public void saveRecord(MyRecord record) {
Session session =
this.getSession();
try {
session.saveOrUpdate(record);
session.flush();
}
catch (HibernateException e) {
e.printStackTrace();
}
finally {
if (session != null) {
try {
session.close();
}
catch (HibernateException e) {
e.printStackTrace();
}
}
}
}
public void removeRecord(Long id) {
Session session =
this.getSession();
try {
MyRecord record = (MyRecord) session.load(MyRecord.
class, id);
session.delete(record);
session.flush();
}
catch (HibernateException e) {
e.printStackTrace();
}
finally {
if (session != null) {
try {
session.close();
}
catch (HibernateException e) {
e.printStackTrace();
}
}
}
}
}

Hibernate uses a properties file or xml file for it's configuration information(datasource url, username, password, database dialect, etc). For simplicity, I'm listing only the necessary information.

#hibernate.properties #only used for MyRecordDAOHibernateTest
hibernate.connection.url = jdbc:hsqldb:mem:test
hibernate.connection.username = sa
hibernate.connection.password =
hibernate.dialect = net.sf.hibernate.dialect.HSQLDialect
hibernate.connection.driver_class = org.hsqldb.jdbcDriver
hibernate.hbm2ddl.auto=create

The unit test to exercise this DAO class is listed below. It is a pretty straight forward JUnit test. The only thing that might look out of the ordinary is that we are instantiating the MyRecordDAOHibernate() class directly. In practice, it would be beneficial to hide this behind a Factory. Then you could more easily allow for alternate implementations.

package com.shoesobjects;
import com.shoesobjects.dao.MyRecordDAO;
import com.shoesobjects.dao.MyRecordDAOHibernate;
import junit.framework.Assert;
import junit.framework.TestCase;
public class MyRecordDAOHibernateTest extends TestCase {
private MyRecord record = null;
private MyRecordDAO dao = null;
protected void setUp() throws Exception {
super.setUp();
dao = new MyRecordDAOHibernate();
}
protected void tearDown() throws Exception {
super.tearDown();
dao = null;
}
public void testSaveRecord() throws Exception {
record = new MyRecord();
record.setFirstName("Gavin");
record.setLastName("King");
dao.saveRecord(record);
Assert.
assertNotNull("primary key assigned", record.getId());
}
}

The version using Hibernate and Spring has a much cleaner implementation thanks to Springs. Notice this version doesn't have to deal with creating the SessionFactory or dealing with opening/closing sessions. The Spring Framework takes care of this under the covers for you. All you have to do is add the SessionFactory as a needed dependency in Spring's applicationContext.xml file. Notice this class is only 26 lines of code. Look at how clear and concise this version is.

package com.shoesobjects.dao;
import com.shoesobjects.MyRecord;
import org.springframework.orm.hibernate.support.HibernateDaoSupport;
import java.util.List;
public class MyRecordDAOHibernateWithSpring extends HibernateDaoSupport
implements MyRecordDAO {
public MyRecord getRecord(Long id) {
return (MyRecord) getHibernateTemplate().get(MyRecord.class, id);
}
public List getRecords() {
return getHibernateTemplate().find("from MyRecord");
}
public void saveRecord(MyRecord record) {
getHibernateTemplate().saveOrUpdate(record);
}
public void removeRecord(Long id) {
Object record = getHibernateTemplate().load(MyRecord.
class, id);
getHibernateTemplate().delete(record);
}
}

The applicationContext.xml file contains all the information for Spring's Bean Factory to instantiate the necessary class es and wire the dependencies. Notice the bean with an id of myRecordDAO, it has a reference to the bean sessionFactory which is also defined in this config file.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans> <bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">

<property name="driverClassName">
<
value>org.hsqldb.jdbcDriver</value></property>
<property name="url">
<
value>jdbc:hsqldb:mem:test</value>
</
property>
<property name="username"><value>sa</value>
</
property>
<property name="password">
<
value></value>
</
property>
</bean>
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref local="dataSource"/>
</
property>
<property name="mappingResources">
<list>
<value>MyRecord.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
net.sf.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory"><
ref
local="sessionFactory"/>
</
property>
</bean>
<bean id="myRecordDAO"
class="com.shoesobjects.dao.MyRecordDAOHibernateWithSpring">
<property name="sessionFactory"><ref local="sessionFactory"/>
</
property>
</bean>
</
beans>

Here is the JUnit test for Hibernate+Spring implementation of the data access object. Notice it is just as straight forward as the last unit test but has one noticeable advantage. Look at line 22 where we instantiate the data access object. No concrete implementation is listed, only the interface. The concrete class is instantiated by Spring's BeanFactory behind the scenes allowing us to swap out the implementation by editing the applicationContext.xml descriptor. This flexibility allows our unit test to easily be modified to use a mock implementation without using command line parameters to the JVC or making code changes.

package com.shoesobjects;
import com.shoesobjects.dao.MyRecordDAO;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyRecordDAOHibernateWithSpringTest extends TestCase {
private ApplicationContext ctx = null;
private MyRecord record = null;
private MyRecordDAO dao = null;
public MyRecordDAOHibernateWithSpringTest() {
// Should put in a parent class that extends TestCase
String[] paths = {
"applicationContext.xml"};
ctx = new ClassPathXmlApplicationContext(paths);
}
protected void setUp() throws Exception {
super.setUp();
dao = (MyRecordDAO) ctx.getBean("myRecordDAO");
}
protected void tearDown() throws Exception {
super.tearDown();
dao = null;
}
public void testSaveRecord() throws Exception {
record = new MyRecord();
record.setFirstName("Rod");
record.setLastName("Johnson");
dao.saveRecord(record);
Assert.
assertNotNull("primary key assigned", record.getId());
}
}

In order to run this example, the following steps are required.
1) Install MySQL, login as root
2) Create a database called myrecord
create database myrecord;
3) Create a database user called myrecord with a password of myrecord.
grant all privileges on myrecord.* to myrecord@localhost identified by 'myrecord' with grant option;
4) Run the unit test's. The database table will be created upon running the test due to the following line in applicationContext.xml
<prop key="hibernate.hbm2ddl.auto">update</prop>
3) Run the unit tests