Friday, August 12, 2011

Creating a server instance and deploying a Java app on Amazon's EC2 Cloud

My previous blog post detailed the process by which I chose to host my web application on the Amazons EC2 Cloud using the Bitnami Tomcat Stack AMI.  This post will describe some of the steps I followed when creating the server instance, connecting to it and deploying a Java .war.

Connecting to your Amazon EC2 server instance

There's no need for me to write step-by-step directions for creating the instance since Bitnami has already done a great job in providing instructions.  It's quite simple and I didn't run into any issues creating the instance.

Don't forget to open the SSH and HTTP ports for your server instance using the Security Groups function of the AWS Management Console.  Once you open port 80 you will be able to access the default Tomcat web page in your browser via your instance's public DNS name (ie. http://ec2-107-20-221-246.compute-1.amazonaws.com).

Connecting to the new server instance is a little trickier, particularly for someone who isn't exactly a Unix/Linux guru.  However, it's still relatively simple and again Bitnami has provided detailed instructions.

They key to connecting to your instance is the key.  :) Well, key pairs to be exact.  It's not just the key pair, you'll also need an SSH client like PuTTy.  You will be prompted to create and download the key when you create your first EC2 server instance.  Keep this file secure and do not lose it.

Here's the part that tripped me up: the key you download from AWS is not compatible with the PuTTy SSH client.  AWS provides a key in .pem format and you'll need to convert it to the .ppk format that PuTTy can use.  This excellent blog post provides details and instructions for converting the key and connecting with PuTTy.

Configuring MySQL

My next step was to access MySQL and create my database schema. I installed Navicat Lite on my desktop and configured it as described by Bitnami here.  This worked like a charm and it wasn't long before I had remotely logged into MySQL running on my server instance on EC2.  I  ran SQL commands to create a database schema, user and grant privileges.  I then imported a .sql file with the DDL I had exported from my local database using MySQL's "mysqldump" tool.

Deploying a .war file to Tomcat

I installed the WinSCP FTP client on my development machine and configured it to connect to my server instance.  Then I FTP'd my .war file to the "/opt/bitami/apache-tomcat/webapps" directory and voila!  My web application was now running on Amazon's EC2.

Getting started with Amazon's Elastic Compute Cloud (EC2) and the Bitnami Tomcat Stack

My next couple of blog posts will detail my experiences thus far with Amazon's Elastic Computing Cloud (Amazon EC2) and the Bitnami TomcatStack Amazon Machine Image (AMI).

Choosing Amazon's EC2

In the past, I have typically deployed my Java applications within a company intranet or extranet.  However,  my current project needs to be publicly accessible and it's up to me to choose a suitable and ultra-affordable web hosting provider.

My hosting requirements are fairly simple.  I want to deploy my application on Apache Tomcat using a MySQL database and several popular Java frameworks including Hibernate, Struts 2, and eventually Spring.  Other than that, I don't expect my current web application to require much in terms of bandwidth and resources.

I was considering trying Google's App Engine until I learned the App Engine doesn't support the Hibernate ORM framework and requires some tweaking to work with Struts 2.

I began to look more closely at Amazon's EC2 service after reading several StackOverflow posts (here and here) suggesting EC2 as an alternative to traditional web hosting services for Java applications.  I soon learned that Amazon offers a Free Usage Tier for new customers.  As described on their website, the "free usage tier can be used for anything you want to run in the cloud: launch new applications, test existing applications in the cloud, or simply gain hands-on experience with AWS."

At first, getting started with EC2 initially seemed a like daunting task.  I explored using Amazon's Elastic Beanstalk which is promoted as "an even easier way for you to quickly deploy and manage applications in the AWS cloud."  However, I couldn't find any documentation or examples of using Beanstalk with MySQL.  The examples I found used the Amazon SimpleDB and it wasn't clear whether I could easily use SimpleDB with Hibernate.  Lacking a clear understanding of how to use Beanstalk with MySQL I decided not to try Beanstalk.  (Note: Please let me know if you have used Beanstalk with MySQL or if you've found some resources on this topic.  Thanks!)

Choosing an Amazon Machine Image (AMI)

After creating an Amazon AWS account, it was time to select and launch a server instance on the EC2.  But which AMI should I select for my EC2 instance?  There are thousands of Community AMI's, hundreds of which are listed as free tier eligible.

Here's a useful blog post I found describing the steps necessary to select and install Java, Tomcat, and MySQL.  The blog author chose the Amazon Linux AMI.  This AMI is a good starting point and is actively supported by Amazon.  "Amazon Web Services also provides ongoing security and maintenance updates to all instances running the Amazon Linux AMI. The Amazon Linux AMI is provided at no additional charge to Amazon EC2 users."

But alas, is there an even easier way to get started?  With all those Community AMI's out there can I find one that bundles Linux, Java, Apache, Tomcat and MySQL.  The answer is yes!  The Bitnami Tomcat Stack "includes ready-to-run versions of Apache, Tomcat, MySQL and Java and required dependencies" and "is distributed for free under the Apache 2.0."  Nice!

Bitnami also offers a Cloud Hosting service which in conjunction with the Amazon EC2 provides a number of ease-of-use benefits.  However, it is not required to use the Bitnami Cloud Hosting service when selecting one of their free Community AMI's.

In my next blog post I'll explain the process of creating and connecting to the Amazon EC2 instance.

Sunday, August 7, 2011

Using Hibernate's event architecture to manage created and updated timestamp fields

This blog post describes a method for managing created and last modified timestamp table columns using Hibernate 3.6.  Before I detail the working solution, I'll explain a failed approach I attempted.  I'm including this approach because it turned out to be useful learning experience.  You can skip to the bottom section of this post if you want to go straight to the working solution. :)

A little Googling found a Stack Overflow post suggesting using @Prepersist and @PreUpdate JPA Annotations.  Up to this point I'd been using Hibernate mapping files (hbm.xml) to define my entities.  I'd been using Ant and the hibernatetool task to generate both the POJO classes and the Database tables.  For the most part I was satisfied using the hbm xml mappings although I had planned on eventually converting to JPA Annotations mappings anyway.

The @Prepersist and @PreUpdate annotations looked simple enough and I decided now was time to bite the bullet and I convert all my .hbm xml mappings to JPA Annotations.  This took some time but was a worthwhile experience.  Maybe I'll write a blog post discussing the process I used for migrating from .hbm xml mapping files to JPA Annotations.

When I was finally satisfied my newly converted JPA Annotated classes were functioning as expected I went ahead and added @Prepersist and @PreUpdate to my entity POJOs as follows:

@Temporal(TemporalType.TIMESTAMP)
@Column(name="created", nullable = false)
public Date getCreated() {return created;}
public void setCreated(Date created) {this.created = created;}
private Date created;

@Temporal(TemporalType.TIMESTAMP)
@Column(name="updated", nullable = false)
public Date getUpdated() {return updated;}
public void setUpdated(Date updated) {this.updated = updated;}
private Date updated;

@PrePersist
protected void onCreate() {
setCreated(new Date());
setUpdated(new Date());}

@PreUpdate
protected void onUpdate() {
setUpdated(new Date());}

I soon realized an important drawback to this approach.  It simply doesn't work when using Hibernate's Session API.  This was a big problem for me since as I've discussed in detail in previous blog posts I am in fact using org.hibernate.Session.  :( As described in this Stack Overflow post, it is necessary to use the EntityManager API when using the @Prepersist and @PreUpdate annotations.

I considered converting from the Session API to the EntityManager API but decided against it for the time being.  That's when I looked more closely at Hibernate's interceptor and events architecture.  I also found a very helpful blog post by a blogger name neildo that explained how to use an event listener to manage last modified date.

Using neildo's blog post and the documentation as a starting point, I developed the following code that I am using today.  My code differs from the neildo's example in that I'm setting both the created and the modified dates in the custom event listener.

First there's the interface that each entity POJO must implement.  As you can see the interface includes "id" which I'll explain later.
public interface AuditableDate {

    public void setId(int id);
    public int getId();
    
    public void setCreated(Date date);
    public Date getCreated();
    
    public void setUpdated(Date date);
    public Date getUpdated();
}

Here's an example of an entity POJO that implements AuditableDate.  For this example, I've stripped out the rest of the entity properties and left only those needed to implement AuditableDate.  Obviously the real entity would have other properties as well.
public class Tag implements AuditableDate, Serializable {

    @Id
    @GeneratedValue
    @Column(name="TagId")
    @Override
    public int getId() {return this.id;}
    public void setId(int id) {this.id = id;}
    private int id;

    @Override
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name="CreatedDate",
            nullable=false, 
        updatable = false)
    public Date getCreated() {return created;}
    public void setCreated(Date date) {this.created = date;}
    private Date created;
    
    @Override
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name="UpdatedDate", nullable = false)
    public Date getUpdated() {return updated;}
    public void setUpdated(Date date) {this.updated = date;}
    private Date updated;

    public Tag() {
    }
}

The event listener is as follows.  You can see where I'm checking the Id property to determine if the we are working with a new record and if so I'm setting the both the Created Date and the Updated Date.
public class SaveOrUpdateDateListener extends DefaultSaveOrUpdateEventListener {
    
    static Logger logger = Logger.getLogger(SaveOrUpdateDateListener.class);

    @Override
    public void onSaveOrUpdate(SaveOrUpdateEvent event) {
        logger.debug("Entering onSaveOrUpdate()");
        
        if (event.getObject() instanceof AuditableDate) {
            AuditableDate record = (AuditableDate) event.getObject();
            
            // set the Updated date/time
            record.setUpdated(new Date());

            // set the Created date/time
            if (record.getId() == 0) {
                record.setCreated(new Date());
            }
        }
}

The last step is to add a reference to the Hibernate configuration so that it knows to fire the custom listener.
<event type="save-update">
    <listener class="org.robbins.persistence.SaveOrUpdateDateListener"/>
    <listener class="org.hibernate.event.def.DefaultSaveOrUpdateEventListener"/>
</event>

You'll notice that I've included BOTH the custom listener and the default listener.  I'm not sure why that was necessary but I was unable to get the default listener to fire in the custom listener by using:
super.onSaveOrUpdate(event);

I hope you found this post useful.  I'd appreciate any questions or suggestions you may have.  Thanks!

Thursday, August 4, 2011

Integrating OpenId into my Struts 2 app - Part 4 - The OpenIdAuthenticator bean

In the final post in this series, we'll concentrate on the OpenIdAuthenticator which does the heavy lifting, with help from the OpenId4Java library, in our interaction with the user's selected OpenID Provider.

According to the OpenId4Java website, the OpenId4Java library "allows you to OpenID-enable your Java webapp."  Many thanks to the folks who put together this solid library.  I suggest checking out the sample code.  There's a number of example that will help you get started.

One of the tricky things I encountered was managing to get the OpenId provider to provide information about the logged in user.  Things like the user's first and last name, their email address, country, and language.  I was disappointed to discover the OpenID Providers (Google, Yahoo, AOL, etc) are not consistent in the level of detail they provide about the user.  Not only that but the format of the attributes are different as well.  This difference leads to the if/else section of the getValidateOpenIdUrl() method.

public class OpenIdAuthenticator {
    
    static Logger logger = Logger.getLogger(OpenIdAuthenticator.class);

    private static ConsumerManager getConsumerManager(Map<String, Object> application) {
        logger.debug("Entering getConsumerManager()");
        
        ConsumerManager manager;

        // try to get the ConsumerManager from the Application scope
        manager = (ConsumerManager)application.get("consumermanager");
        
        if (manager == null) {
            // create a new ConsumerManager
            try {
                manager = new ConsumerManager();
                manager.setAssociations(new InMemoryConsumerAssociationStore());
                manager.setNonceVerifier(new InMemoryNonceVerifier(5000));
            } catch (Exception e) {
                e.printStackTrace();
            }
        
            // add the Consumer Manager to the application scope
            application.put("consumermanager", manager);
        }

        return manager;
    }
    
    @SuppressWarnings("unchecked")
    public static String getValidateOpenIdUrl(String returnUrl, String openIdIdentifier, Map<String, Object> httpSession, Map<String, Object> application) throws DiscoveryException,
            MessageException, ConsumerException {
        
        logger.debug("Entering getOpenIdDestinationUrl()");

        // get a reference to the Consumer Manager
        ConsumerManager manager = getConsumerManager(application);
        
        // perform discovery on the user-supplied identifier
        List<DiscoveryInformation> discoveries = manager.discover(openIdIdentifier);

        // attempt to associate with the OpenID provider
        // and retrieve one service endpoint for authentication
        DiscoveryInformation discovered = manager.associate(discoveries);

        // store the discovery information in the user's session for later use
        // leave out for stateless operation / if there is no session
        httpSession.put("discovered", discovered);

        // obtain a AuthRequest message to be sent to the OpenID provider
        AuthRequest authReq = manager.authenticate(discovered, returnUrl);

        // Attribute Exchange
        FetchRequest fetch = FetchRequest.createFetchRequest();

        // different Open Id providers accept different attributes
        if (openIdIdentifier.contains("google.com")) {
            logger.debug("Open Id Identifier is: google.com");
            
            fetch.addAttribute("first", "http://axschema.org/namePerson/first", true);
            fetch.addAttribute("last", "http://axschema.org/namePerson/last", true);
            fetch.addAttribute("email", "http://axschema.org/contact/email", true);
            fetch.addAttribute("language", "http://axschema.org/pref/language", true);
        }
        else if (openIdIdentifier.contains("yahoo.com")) {
            logger.debug("Open Id Identifier is: yahoo.com");
            
            fetch.addAttribute("fullname", "http://axschema.org/namePerson", true);
            fetch.addAttribute("nickname", "http://axschema.org/namePerson/friendly", true);
            fetch.addAttribute("email", "http://axschema.org/contact/email", true);
            fetch.addAttribute("language", "http://axschema.org/pref/language", true);
        }
        else if (openIdIdentifier.contains("aol.com")) {
            logger.debug("Open Id Identifier is: aol.com");

            fetch.addAttribute("first", "http://axschema.org/namePerson/first", true);
            fetch.addAttribute("last", "http://axschema.org/namePerson/last", true);
            fetch.addAttribute("email", "http://axschema.org/contact/email", true);
            fetch.addAttribute("language", "http://axschema.org/pref/language", true);
        }
        else {
            logger.debug("Open Id Identifier is: something else");
            
            fetch.addAttribute("fullname", "http://schema.openid.net/namePerson", true); 
            fetch.addAttribute("email", "http://schema.openid.net/contact/email", true); 
            fetch.addAttribute("country", "http://axschema.org/contact/country/home", true);    
        }

        // attach the extension to the authentication request
        authReq.addExtension(fetch);

        logger.info("The request string is: " + authReq.getDestinationUrl(true).replaceAll("&", "\n"));

        return authReq.getDestinationUrl(true);
    }
    
    public static User getAuthenticatedUser(Map<String,String[]> parmList,
            final StringBuffer receivingURL, Map<String, Object> httpSession, Map<String, Object> application)
            throws MessageException, DiscoveryException, AssociationException {

        logger.debug("Entering getAuthenticatedUser()");

        // extract the parameters from the authentication response
        // (which comes in as a HTTP request from the OpenID provider)
        ParameterList openidResp = new ParameterList(parmList);
        
        // retrieve the previously stored discovery information
        final DiscoveryInformation discovered = (DiscoveryInformation) httpSession.get("discovered");

        // get a reference to the Consumer Manager
        ConsumerManager manager = getConsumerManager(application);
        
        // verify the response
        final VerificationResult verification = manager.verify(receivingURL.toString(), openidResp, discovered);
        
        // examine the verification result and extract the verified identifier
        Identifier verified = verification.getVerifiedId();
        if (verified == null) {
            return null;
        }

        AuthSuccess authSuccess = (AuthSuccess) verification.getAuthResponse();

        User user = new User();
        user.setOpenid(authSuccess.getIdentity());
        
        if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
            logger.info("Processed as OPENID_NS_AX");
            
            FetchResponse fetchResp = (FetchResponse) authSuccess.getExtension(AxMessage.OPENID_NS_AX);

            // populate the User object with attributes from the FetchResponse
            user.setNickname(fetchResp.getAttributeValue("nickname"));
            user.setEmail(fetchResp.getAttributeValue("email"));
            user.setFullName(fetchResp.getAttributeValue("fullname"));
            user.setFirstName(fetchResp.getAttributeValue("first"));
            user.setLastName(fetchResp.getAttributeValue("last"));
            user.setLanguage(fetchResp.getAttributeValue("language"));
            user.setCountry(fetchResp.getAttributeValue("country"));

            logger.info("User: " + user.toString());
        }

        if (authSuccess.hasExtension(SRegMessage.OPENID_NS_SREG)) {
            logger.info("Processed as OPENID_NS_SREG");
            
            SRegResponse sregResp = (SRegResponse) authSuccess.getExtension(SRegMessage.OPENID_NS_SREG);

            // if we didn't get the user's email addy from the FetchResponse, try to get it from the SRegResponse 
            if (StringUtils.isBlank(user.getEmail())) {
                user.setEmail(sregResp.getAttributeValue("email"));
            }
        }
        return user;
    }
}

Integrating OpenId into my Struts 2 app - Part 3 - The LoginAction Action class

The LoginAction class controls the flow between the user's authentication request and the business bean that does our heavy lifting.  Aside from a Logout method that removes the user's http session, the two main methods in this class are as follows:
  • validateOpenId() - This method is called after the user selects an OpenID provider from the OpenID Selector component on the Login form.  The Action then calls the business bean (OpenIdAuthenticator) which generates the destination URL for OpenId Provider.  The request is then redirected to the destination URL.  It's important to note that we are now redirecting the user away from our web application.  Our application never sees or has access to the user's password.  The OpenID Provider will let us know if the use has successfully authenticated.
  •  validateOpenId() - Once the user has successfully authenticated with the OpenID Provider.  The Provider redirects the user back to our web application, using the return URL we provided.  This method processes that request.  Again we pass it off to the business bean and let it confirm the login was successful and glean any relevant information sent by the OpenID Provider.  The business bean will return a User object which we'll persist in the database and add to the user's HTTP Session.  At this point we'll redirect the user to the resource he was originally requesting before we forced him to authenticate.
Here's the Action class:
public class LoginAction extends FlashCardsAppBaseAction implements Preparable, ServletRequestAware, ServletResponseAware, SessionAware, ApplicationAware {

    static Logger logger = Logger.getLogger(LoginAction.class);

    // this is the only form field we will be looking for from OpenID Selector on the front end
    private String openid_identifier;

    // Hibernate Session
    private Session hibernateSession;
    
    // we'll need access to the Servlet spec objects, rather than just their attribute or parm maps
    private HttpServletRequest request;
    private HttpServletResponse response;
    
    // we'll be storing the User object in the Session
    private Map<String, Object> httpSession;
    
    // the OpenIdAuthenticator class needs access to the application to store a OpenId4Java related object
    private Map<String, Object> application;
    
    // we'll need to send this to the OpenId provider so it knows where to send its response 
    private final String returnAction = "/home/authenticateOpenId.action";

    // the OpenID Selector form will submit to this Action method
    public String validateOpenId() throws Exception {

        logger.debug("Entering validateOpenId()");
        
        // get rid of trailing slash
        if (getOpenid_identifier().endsWith("/")) {
            setOpenid_identifier(getOpenid_identifier().substring(0, getOpenid_identifier().length() - 1));
        }

        logger.debug("The requested OpenId identifier is: " + getOpenid_identifier());

        // determine a return_to URL where the application will receive
        // the authentication responses from the OpenID provider
        String returnUrl = getServerContext(request) + returnAction;
        
        // construct the destination Url to send to the Open Id provider
        String destinationUrl = OpenIdAuthenticator.getValidateOpenIdUrl(returnUrl, this.getOpenid_identifier(), httpSession, application); 
        
        // redirect to the Auth Request
        response.sendRedirect(destinationUrl);
        
        // no need to return a view
        return NONE;
    }
    
    public String authenticateOpenId() throws Exception {
        logger.debug("Entering authenticateOpenId()");

        Map<String,String[]> parmList = request.getParameterMap();

        // extract the receiving URL from the HTTP request
        final StringBuffer receivingURL = request.getRequestURL();
        final String queryString = request.getQueryString();

        if (queryString != null && queryString.length() > 0) {
            receivingURL.append("?").append(request.getQueryString());
        }
        
        logger.debug(receivingURL.toString().replaceAll("&", "\n"));

        // verify the user has authenticated with the Open Id provider and 
        // get a reference to the authenticated User
        User user = OpenIdAuthenticator.getAuthenticatedUser(parmList, receivingURL, httpSession, application);

        // save the user to the DB
        UserPersister uPersister = new UserPersister();
        uPersister.saveOrUpdateUser(user, hibernateSession);
        
        // add the user to the HTTP Session
        httpSession.put("user", user);
        
        // retrieve the original URL from the Session
        String desitinationURL = (String)httpSession.get("originalURL");

        // was a destination URL provided?
        if (desitinationURL == null) {
            logger.debug("No destination URL provided, will send to Home");
            return "home";
        }
        else {
            logger.debug("Redirecting to : " + desitinationURL);
            response.sendRedirect(desitinationURL);
            return NONE;
        }
    }

    @SuppressWarnings("rawtypes")
    public String logout() {
        logger.debug("Entering logout()");
        
        try {
            // invalidate the user's session
            httpSession.remove("user");

            if (httpSession instanceof org.apache.struts2.dispatcher.SessionMap) {
                try {
                    ((org.apache.struts2.dispatcher.SessionMap) httpSession).invalidate();
                } catch (IllegalStateException e) {
                    logger.error("Exception in logout()", e);
                }
            }
            
            return "success";
        } catch (Exception e) {
            logger.error("Exception in logout():", e);
            
            return "error";
        }
    }

    private String getServerContext(final HttpServletRequest request) {
        // Get the base url.
        final StringBuilder serverPath = new StringBuilder();
        
        serverPath.append(request.getScheme() + "://");
        serverPath.append(request.getServerName());

        if (request.getServerPort() != 80) {
            serverPath.append(":" + request.getServerPort());
        }
        serverPath.append(request.getContextPath());
        
        return serverPath.toString();
    }
    
    @Override
    public void prepare() throws Exception {
        logger.debug("Entering prepare()");

        hibernateSession = getHibernateSession();
    }

    public String getOpenid_identifier() {
        return openid_identifier;
    }

    public void setOpenid_identifier(String openid_identifier) {
        this.openid_identifier = openid_identifier;
    }

    @Override
    public void setSession(Map<String, Object> httpSession) {
        this.httpSession = httpSession;
    }
    
    @Override
    public void setServletResponse(final HttpServletResponse response) {
        this.response = response;
    }

    @Override
    public void setServletRequest(final HttpServletRequest request) {
        this.request = request;
    }

    @Override
    public void setApplication(Map<String, Object> application) {
        this.application = application;
    }
}

In the final post in this series, I'll include the code for the OpenIdAuthenticator business bean which does the heavy lifting and relies heavily on the OpenId4Java library.

Integrating OpenId into my Struts 2 app - Part 2 - Javascript OpenID Selector

Now that we're integrating OpenID authentication into our website, we'll need a way for users to select an OpenID provider.  According to Wikipedia ,there are over two dozen OpenID Providers.  It would seem a daunting task to create an attractive web form where a user can select from a list of approved providers.

Thankfully, the openid-selector project already exists and provides a "a user-friendly way to select an OpenID.  It has been designed so that users do not even need to know what OpenID is to use it, they simply select their account by a recognisable logo."

This component is quite easy to use and I was able to integrate it into my application very easily.

Here's the code from my JSP:
    <%@ taglib prefix="s" uri="/struts-tags"%>
    
    <s:actionerror theme="jquery" />
    <s:fielderror theme="jquery" />

    <h2>Login to the Flash Cards Application</h2>
    <br/>
    <!-- Simple OpenID Selector -->
    <s:form action="validateOpenId" namespace="/home" method="get" id="openid_form">
        <input type="hidden" name="action" value="verify" />
        <fieldset>
            <legend>Sign-in or Create New Account</legend>
            <div id="openid_choice">
                <p>Please click your account provider:</p>
                <div id="openid_btns"></div>
            </div>
            <div id="openid_input_area">
                <input id="openid_identifier" name="openid_identifier" type="text" value="http://" />
                <input id="openid_submit" type="submit" value="Sign-In"/>
            </div>
            <noscript>
                <p>OpenID is service that allows you to log-on to many different websites using a single indentity.
                Find out <a href="http://openid.net/what/">more about OpenID</a> and <a href="http://openid.net/get/">how to get an OpenID enabled account</a>.</p>
            </noscript>
        </fieldset>
    </s:form>
    <!-- /Simple OpenID Selector -->

Take note of the"openid_identifier" field.  This field hold the value of the selected OpenID Provider and we'll reference it in both the Struts Interceptor and the Authentication class.

Here's a screenshot of the form in my application:
In my next post, I'll discuss the Action class I'm using to "control" login traffic.

Integrating OpenId into my Struts 2 app - Part 1 - Interceptors

My next few blog posts will detail the integration of OpenID authentication into my application. The OpenId website describes the benefits of using OpenId.  As a web site user myself, I'm always relieved when I'm able to reuse my existing Google account when creating new a profile on a website.  That's one less User Id and Password I'll need to write down or remember.  I also appreciate that I'm safely authenticating with the OpenId provider and the underlying website never has access to my password.

Before I begin, I want to list and give props to several websites that provided invaluable information and code samples that greatly assisted me in integrating OpenId into my own application.

Struts 2 Interceptors

OpenId4Java Code samples

I'm not going into detail on how Interceptors work in Struts 2.  I suggest reading the relevant Struts 2 docs and the links I posted above.

Here's the code for the LoginInterceptor:
public class LoginInterceptor extends AbstractInterceptor implements StrutsStatics{
    
    static Logger logger = Logger.getLogger(LoginInterceptor.class);

    public String intercept(final ActionInvocation invocation) throws Exception {
        logger.debug("Entering intercept()");

        String invocatedAction = invocation.getAction().getClass().getName();
        logger.debug("Invocated Action: " + invocatedAction);

        // get references to the App Context, Session, and Request objects
        final ActionContext context = invocation.getInvocationContext ();
        HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
        HttpSession session =  request.getSession (true);

        // Is there a "user" object stored in the user's HttpSession?
        User user = (User)session.getAttribute("user");
        
        if (user == null) {
            // The user has not logged in yet.
            logger.debug("User NOT found in the Session");

            // Is the user attempting to log in right now?
            String loginIdentifier = request.getParameter("openid_identifier");
            String openIdEndpoint =  request.getParameter("openid.op_endpoint");

            // we can know the user is trying to login right now if the "openid_identifier" is a Request parm
            if (! StringUtils.isBlank (loginIdentifier) ) {
                // The user is attempting to log in.
                logger.debug("The user is attempting to log in");

                // Process the user's login attempt.
                return invocation.invoke ();
            }
            // we know the user has just auth'd with the OpenID provider if the "openid.op_endpoint" is a Request parm
            else if(! StringUtils.isBlank (openIdEndpoint) ) {
                // The user has logged in with an OpenId provider
                logger.debug("The user has logged in with an OpenId provider");

                // Process the user's login attempt.
                return invocation.invoke ();
            }
            else {
                // save the original URL, we'll need it later
                saveReceivingURL(request, session);
                
                logger.debug("Forwarding to the Login form");
            }

            // it we get this far then the user hasn't tried to login yet, 
            // and we need to send to the login form.
            return "login";
        } 
           else {
               logger.debug("User " + user.toString() + " found in the Session");
               
               // user is already logged in
               return invocation.invoke ();
        }
    }

    private void saveReceivingURL(HttpServletRequest request, HttpSession session) {
        logger.debug("Entering saveReceivingURL()");
        
        // extract the receiving URL from the HTTP request
        final StringBuffer receivingURL = request.getRequestURL();
        final String queryString = request.getQueryString();

        // if there is a query string then we'll need that too
        if (queryString != null && queryString.length() > 0) {
            receivingURL.append("?").append(request.getQueryString());
        }
        
        logger.debug("Original URL: " + receivingURL.toString());
        
        // save the original URL in the Session
        // we're going to need to redirect the user back to this URL after login is completed
        session.setAttribute("originalURL", receivingURL.toString());
    }
}

The LoginInterceptor is invoked for every request and checks the http session to see if the user is already logged in.  If the user is found in the session, the Interceptor simply returns invocation.invoke () and allows the request to process normally.

If the user is not found in the session then we need to interrupt the request and force the user to authenticate before returning to the originally requested resource.  However, it's not as simple as just forwarding the user to the login page.  Remember, the Interceptor is invoked on every single request and we need to account for the following:
  • Is the user presently already trying to login?  If so, there's no need to interrupt the request because we're already going to the login form.
  • Is the request coming from the OpenId Provider (Google, Yahoo, etc)?  If so, again there's not need to interrupt the request.  Allow the request to proceed to our app's authentication code where we'll work with the response from the OpenId Provider.
  • Otherwise, we'll go ahead and interrupt the request and forward the user to the login form where they can choose which OpenId Provider they want to use for authentication.  Note: We'll need to keep a reference to the original requested resource so that we can send the user to it when the authentication is completed.
Now that we have the code for our Interceptor, we'll need add it to our Struts configuration and modify the Interceptor Stack.

        <interceptors>
            <interceptor name="hibernateSession" class="org.robbins.flashcards.presentation.HibernateSessionInterceptor"/>
            <interceptor name="loginInterceptor" class="org.robbins.flashcards.presentation.LoginInterceptor"/>

               <interceptor-stack name="defaultStackWithStore">
                   <interceptor-ref name="hibernateSession"/>
                   <interceptor-ref name="loginInterceptor"/>
                <interceptor-ref name="store">
                    <param name="operationMode">STORE</param>
                </interceptor-ref>
                <interceptor-ref name="defaultStack" />
            </interceptor-stack>
            
               <interceptor-stack name="defaultStackWithRetrieve">
                   <interceptor-ref name="hibernateSession"/>
                   <interceptor-ref name="loginInterceptor"/>
                <interceptor-ref name="store">
                    <param name="operationMode">RETRIEVE</param>
                </interceptor-ref>
                <interceptor-ref name="defaultStack" />
            </interceptor-stack>
        </interceptors>

Since I'm also using the "store" interceptor I've had to configure two interceptor stacks to deal with the Storing and Retrieving operations.  Below is an example of a an action declaration:

        <action name="*"
                class="org.robbins.flashcards.presentation.FlashCardAction"
                method="{1}">
               <interceptor-ref name="defaultStackWithRetrieve"/>
            <result name="success" type="tiles">{1}flashcard.tiles</result>
            <result name="error" type="tiles">error.tiles</result>
        </action>

In my next post, I'll discuss the JQuery OpenID Selector component into my Login form.

Monday, June 13, 2011

Hibernate - Part 7 - Closing Hibernate Sessions using Open Session in View pattern in Struts2

My initial approach for creating and storing the Hibernate SessionFactory worked fine until I added c3p0 connection pooling as described in my last blog post.

It wasn't long after enabling c3p0 that my application began regularly hanging.  The app would hang every time it reached the maximum connection pool size (c3p0.max_size), which was currently configured for 20 connections.  Why were my connections not getting reused?  As I examined my code for creating Hibernate SessionFactory and Hibernate Sessions, I realized that I was creating Sessions but never closing them.  Therefore, the app was keeping connections open and creating a new connection for each request until it maxed out.

In an effort to solve this problem, I added a session.close() in a finally block in the Struts 2 Action class method.  This only succeeded in throwing "LazyInitializationException" errors in my app as follows:
ERROR org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: org.robbins.flashcards.model.Tag.flashcards, no session or session was closed
The LazyInitializationException gets thrown when Hibernte tries to access the database but your Session is already closed.  In my case, this was occurring in my View (JSP).  This is an expected problem and is explained in detail in this article in the Hibernate community.

The article proposes the Open Session in View (OSIV) pattern as a commonly used solution to this issue in two-tier environments.  The Open Session in View pattern keeps the Hibernate Session open until the View has completed rendering.  This pattern is not without its drawbacks.  For more information on the advantages and disadvantages of this approach I recommend reading this discussion on StackOverflow and this blog post by Chris Upton.

At this point, I decided to replace my existing solution for storing the Hibernate SessionFactory with a OSIV approach using a custom Struts 2 interceptor:

public class HibernateSessionInterceptor extends AbstractInterceptor    {

    /**
     * Holds the current hibernate session, if one has been created.
     */
    protected static ThreadLocal <Session> hibernateHolder = new ThreadLocal <Session> (); 
  
    protected static SessionFactory factory;
    
    static Logger logger = Logger.getLogger(HibernateSessionInterceptor.class);
  
    public String intercept(final ActionInvocation invocation) throws Exception {
        logger.debug("Entering intercept()");

        if (factory == null) {
            logger.debug("Hibernate SessionFactory is null.  Creating new SessionFactory.");
            factory = new Configuration().configure().buildSessionFactory();
        }
        
        // if a Hibernate Session doesn't already exist in the ThreadLocal
        if (hibernateHolder.get() == null) {
            logger.debug("Hibernate Session not found in ThreadLocal");

            // get a Hibernate Session and place it in ThreadLocal
            getSession();
        }
        
        try {  
            // invoke the Action
            return invocation.invoke();
        }
        finally {
            logger.debug("Entering finally{} block of intercept()");
            
            Session sess = (Session)hibernateHolder.get( );

            if (sess != null) {
                logger.debug("Hibernate Session found in ThreadLocal.  Setting Session to null in ThreadLocal.");
                
                hibernateHolder.set(null);
              
                try {
                    logger.debug("Closing Hibernate Session");
                    sess.close( );
                }
                catch (HibernateException ex) { 
                    logger.error("Exception in doFilter():", ex);
                    throw new ServletException(ex);
                }
            }
            else {
                logger.debug("Could not find Hibernate session");
            }
        }
    }
  
    public static Session getSession( ) throws HibernateException {
        logger.debug("Entering getSession()");
        
        Session sess = (Session)hibernateHolder.get();
      
        if (sess == null) {
            logger.debug("Getting a Hibernate Session from the SessionFactory and adding to ThreadLocal");
            
            sess = factory.openSession( );
            hibernateHolder.set(sess);
        }
        else {logger.debug("Hibernate Session found in ThreadLocal");}
        
        return sess;
    }
}

The SessionFactory is stored as a static variable and the Session is stored in ThreadLocal.  This solves my initial problem of maxing out the connection pool by explicitly closing the Session in the "finally" block of the Struts 2 interceptor.

Friday, June 10, 2011

Hibernate - Part 6 - c3p0 connection pooling

In an earlier post, I explained a technique for storing a Hibernate SessionFactory in the ServletContex.  The objective was to create the Hibernate SessionFactory once and add it to the ServletContext so that it is available for the life of the application.

This solution seemed sufficient for my needs and I moved on to other issues.  However, I soon noticed my application would hang when left idle overnight.  It wasn't immediately clear what caused this behavior but as I looked through the logs I found the following error.

WARN org.hibernate.util.JDBCExceptionReporter - SQL Error: 0, SQLState: 08S01
ERROR org.hibernate.util.JDBCExceptionReporter - The last packet successfully received from the server was 49,245,917 milliseconds ago.
The last packet sent successfully to the server was 49,245,917 milliseconds ago. is longer than the server configured value of 'wait_timeout'.
You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
I found an informative blog post explaining the root of this problem.  The author explains that MySQL is dropping the connection after the idle timeout expires and by default Hibernate is not able reconnect on its own.  The solution is to use connection pooling, something I was planning on implementing eventually anyway.  The Hibernate documentation addresses this issue as follows:
Hibernate's own connection pooling algorithm is, however, quite rudimentary. It is intended to help you get started and is not intended for use in a production system, or even for performance testing. You should use a third party pool for best performance and stability. Just replace the hibernate.connection.pool_size property with connection pool specific settings. This will turn off Hibernate's internal pool. For example, you might like to use c3p0.
 Adding c3p0 connection pooling was as easy as adding the jar to my classpath and adding the following into my hibernate.cfg.xml

        <!-- Use the C3P0 connection pool provider -->
        <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
        <property name="hibernate.c3p0.min_size">5</property>
        <property name="hibernate.c3p0.max_size">10</property>
        <property name="hibernate.c3p0.timeout">1800</property>
        <property name="hibernate.c3p0.max_statements">50</property>
        <property name="hibernate.c3p0.idle_test_period">900</property>

And voila, we now have connection pooling.  I was no longer getting the MySQL errors noted at the beginning of this post.

Unfortunatly, I was about to run into another set of issues related to using connection pooling and my existing strategy for creating the Hibernate SessionFactory once and adding it to the ServletContext.  More on that in my next post.

Wednesday, May 4, 2011

Dealing with a tricky ognl.MethodFailedException

I'm trying to reuse an existing JSP form for both creating and editing an entity.  In my case, it's an entity named Tag (not to be confused with JSP tags).  I had everything in working order with Struts 2.2.1.1 for creating my entities and was just beginning the process of integrating edit / update capability when I encountered the tricky error (actually a warning) described below.

The first thing I did in an attempt to add edit capability was add a hidden "id" field to my JSP form so I can keep track of which entity I'm updating.  Since I'm reusing the same form for both creating and editing my entity, the "id" will be submitted without a value on each post for creating a new Tag. Here's what I added to my JSP:
<s:hidden name="id" />
After making this small change I did a function test to make sure I could still create new Tags and saw the following stack trace in the log as excerpted below:
com.opensymphony.xwork2.ognl.OgnlValueStack - Error setting expression 'id' with value '[Ljava.lang.String;@100ac03'
ognl.MethodFailedException: Method "setId" failed for object org.robbins.flashcards.model.Tag@1b9eb34 [name='null' ]
[java.lang.NoSuchMethodException: org.robbins.flashcards.model.Tag.setId([Ljava.lang.String;)] at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1285)
It's complaining because ognl is not finding a setId() method on my POJO that takes a String value.  Well, since "id" is an integer, of course my POJO won't accept a String.  Remember, I'm getting this error when trying to create a new Tag and the hidden "id" field is going to be empty at this point.  It gets submitted as empty, I mean literally an empty string ("").  Ognl it trying to assign a string to an integer field and is complaining when it doesn't find an appropriate method on my POJO.  Maybe this is by design.

I wasn't sure how to deal with this issue.  As usual, I did some Googling but this time I didn't find a suitable solution to my problem.  Next, I posted to StackOverflow and the Struts User distribution list.

A response on StackOverflow suggested it might be an old bug in ognl and to try using a newer version of the ognl jar.  I was already using ognl-3.0.jar which is included in the Struts 2.2.1.1 distribution.  In any case, I replaced it with ognl-3.0.1.jar which I obtained from the Maven2 repository and continued receiving the same error.

My post to Struts User didn't garner any traction at first so I decided to try some more Googling.  I found something that sparked a light bulb in my head.  I found a blog post in Chinese which, although I couldn't read the explanation, appeared to suggest modifying the logging level for ognl packages from Warn to Error.

Could it be that simple?  After all, the stack trace in the log was a Warn and not an Error.  Maybe I could just ignore it.

On further inspection, my create code was actually completing successfully even with the stack trace.  I then finished modifying my JSP, Action, and business tier code to enable editing of existing Tag entities.  Lo and behold, when submitting the edit form, which by the way has a value in the hidden "id" form tag, the "id" is successfully set on my POJO.  Ognl was only throwing the stack trace when the "id" field had no value.

The post to the Struts User List later received some responses and one of the folks suggested to "default the value or make sure you supply a value for your hidden field and base your application logic on this value".

In the end I just modified my log4j.properties as follows:
# Struts OgnlUtil issues unimportant warnings
log4j.logger.com.opensymphony.xwork2.util.OgnlUtil=error
log4j.logger.com.opensymphony.xwork2.ognl.OgnlValueStack=error 

 What do you think?  Prefer a different approach?  Please feel free to comment.

Monday, May 2, 2011

Creating a Struts 2 "Welcome" page

Creating a "welcome" page in a Java web application is usually a simple matter.  Just modify the web.xml with your <welcome-file> and away you go.  However, it's not so simple to have my <welcome-file> point to a Struts 2 Action.  It seems the web container is expecting a JSP or HTML file in the <welcome-file> tag.

I did some Googling and read some posts on stackoverflow and theserverside on this same issue.  There were several suggested approaches.  The two most common suggestions were:
  1. Create a index.jsp and issue a <% response.sendRedirect(myaction.do); %> or alternatively a <jsp:forward page="/myaction.do" />.  Of course you'll also need to add index.jsp as a <welcome-file> in your web.xml.
  2. Create an empty file named welcome.do.  Add welcome.do to your <welcome-file> tag and create a Struts Action mapping for welcome.do.  This is a nice little hack and Struts will go ahead and serve your Action.
I decided to use the second suggestion in my Struts 2 web application.  Here's the steps I used.
  1. Create an empty file named "welcome.action" in "FlashCardsWeb\WebContent"
  2. Modified the web.xml as follows:
    <welcome-file-list>
        <welcome-file>welcome.action</welcome-file>
    </welcome-file-list>
  3. Modified the struts.xml to create the following Action.  "baseLayout" is Tiles definition that includes my header, menu, body, and footer.
    <action name="welcome">
        <result name="success" type="tiles">baseLayout</result>
    </action>
I'm sure there are other approaches.  What are you using?  Feel free to comment with your comments or solution.  Thanks!

Tuesday, April 26, 2011

Testing Struts 2 Actions with JUnit

I've been trying my best to stick closely to the Test-Driven Development process with my current project.  A couple of my previous blog posts detail my experiences setting up JUnit and later testing Hibernate code with JUnit.

I'm currently working with Struts 2 and I have been researching methods for testing my Actions.  The struts.apache.org website has a nice little tutorial detailing the StrutsTestCase and the Struts 2 JUnit plugin.  I searched for more online resources on this topic but it became quickly apparent that most examples include Stuts 2, JUnit and Spring.  I'm not using Spring in this project and I haven't had the opportunity to get up to speed on the Spring framework yet.  Therefore, a lot of the examples I found were not helpful for me right now.

One concern I had about using the StrutsTestCase is that its Sourceforge project page hasn't been updated in years.  It still proudly displays "Now supporting Struts 1.2 and 1.3!"  However, I decided to give it a try since the Struts 2 documentation offers a tutorial and the struts2-junit-plugin-2.2.1.1.jar is included in the current Struts 2 distribution.

I modified the sample code from the Struts 2 tutorial to suit my needs.

public void testCreateTagFail() throws Exception {
    request.setParameter("name", "");
    ActionProxy proxy = getActionProxy("/createtag.action");
    TagAction tagAction = (TagAction) proxy.getAction();
    proxy.execute();

    assertTrue("Problem There were no errors present in fieldErrors but there should have been one error present", tagAction.getFieldErrors().size() == 1);
    assertTrue("Problem field 'name' not present in fieldErrors but it should have been",
            tagAction.getFieldErrors().containsKey("name") );

}

I had some compile errors and needed to add spring-test-2.5.6.jar and spring-core-2.5.6.jar to my build path.  Both of these jars are included in the Struts 2.2.1.1 distribution.

The method above is quite simple and just tests the Action's validation() method to confirm it returns a FieldError when a value for the Tag Name is missing.

Below is a method that gave me a lot of trouble:
    public void testCreateTag() throws Exception {

        request.setParameter("name", "my tag name");
        ActionProxy proxy = getActionProxy("/createtag.action");
        TagAction tagAction = (TagAction) proxy.getAction();
        String result = proxy.execute();

        assertTrue("Problem There were errors present in fieldErrors but there should not have been any errors present", tagAction.getFieldErrors().size() == 0);
        assertEquals("Result returned form executing the action was not success but it should have been.", "success", result);
    }

This JUnit test caused the Action to throw a Null Pointer exception when I tried to run it.  The problem is the Action expects to find a Hibernate SessionFactory object in the ServletContext.  (This earlier blog post explains my methodology for adding the Hibernate SessionFactory to the ServletContext.)  Since the StrutsTestCase is running outside a real container, using a mock container, the Action is not finding the SessionFactory and returning Null instead.

I did some research and determined I had a few options to resolve this problem.  First, as I mentioned in a previous blog post, I would be better off using mock database objects or DBUnit rather than testing against Hibernate and my actual relational database.  I may do that in the future, but it's not going to happen now.  :)

Another option would be to add the SessionFactory to the mock ServletContext and pass that to the Action through the StrutsTestCase.  This is an option I probably should have pursued and I would love to see some example code for this.

Of course, I went with the third and probably least elegant option.  I created a base class that extends ActionSupport and added a method getSession() which tries to retrieve the the Hibernate SessionFactory from the ServletContext and creates a new one if it fails.
public class FlashCardsAppBaseAction extends ActionSupport{

    protected Session getSession() {

        //try to get hibernate session from the servlet context
        SessionFactory sessionFactory =

        (SessionFactory) ServletActionContext.getServletContext()
            .getAttribute(FlashCardAppConstants.HibernateSessionFactoryName);

        // if the sessionFactory is null then create a new one
        if (sessionFactory == null) {
            sessionFactory = HibernateUtil.createSessionFactory();

            // add the Hibernate SessionFactory to the ServletContext
            ServletActionContext.getServletContext().setAttribute(FlashCardAppConstants.HibernateSessionFactoryName, sessionFactory);
                }

        // get a session from the session factory
        return sessionFactory.openSession();
        }
}

I refactored my Actions to extend the new FlashCardsAppBaseAction and they get a handle to the SessionFactory using the super.getSession() method as seen below.

@SkipValidation
public String listTags() {

    try {

        // get a handle to a Hibernate session
        Session session = getSession();

        // get a list of Tags from the data tier
        TagPersister tagPersister = new TagPersister();

        tagList = tagPersister.getTags(session);

        return "success";

    } catch (HibernateException e) {

        logger.error("Exception in listTags():", e);
    
        return "input";
    }
}

The code now works when running in an actual servlet container and also through the mock container via the JUnit tests.

One last note, my new StrutsTestCase tests ran successfully through the Eclipse IDE but were failing when run through ANT outside of Eclipse.  I fixed this by adding the Tomcat jars to the Classpath for the Ant build.xml.

Monday, April 25, 2011

Struts 2 - A simple server-side validation example

There are many options for performing validation in Struts 2.  Below are steps I used to add server-side validation to a Struts v2.2.1.1 web project.
  1. Add validate() method to Action class.  Since my Action classes already extend ActionSupport, it's simple to override the default validate() implementation to provide my custom validations.
    public void validate() {
        logger.debug("Entering validate()");

        if ( tag.getName().length() == 0 ){
            addFieldError("name", getText("error.tag.name"));
        }
    }
    This example simply verifies the "name" attribute of the POJO named Tag exists, adding a FieldError when it doesn't.  Note the getText("error.tag.name") where we call the getText() method of ActionSupport to retrieve the value of the "error.tag.name" message key from the resource bundle.
  2. Add "input" result to struts.xml.  If our validation fails, the validate() method will automatically seek the "input" result.  Therefore, we'll add it to our action element in struts.xml.
    <action name="createtag"
               class="org.robbins.flashcards.presentation.TagAction"
               method="createTag">
        <result name="success">displayTag.jsp</result>
        <result name="input">tagForm.jsp</result>
    </action>
  3. Add <s:actionerror /> and <s:head /> to JSP's. The <s:actionerror /> will render any errors as HTML.  The <s:head /> is optional but will include a CSS (xhtml by default) and a JavaScript utility file.
  4. Add error messages to resource bundle.  In my case, I added  error.tag.name= Tag name is required to my ApplicationResources.properties.  Remember, we added a reference to error.tag.name in our validation code of the Action class.
  5. Add @SkipValidation to methods that don't require validation.  If your Action class is very simple with just a single execute() method then you can skip this step.  However, if you are using several methods in your action classes (ie. create(), edit(), list()) than you may want to add the @SkipValidation annotation to methods you don't need validated.  In my case, I had a list() and display() method that didn't require validation.

Sunday, April 24, 2011

Hibernate - Part 5 - Storing Hibernate SessionFactory with Struts

I'm working on a relatively simple web project using Struts 2 and Hibernate.  I'm not planning on using any EJB's so my persistence code will be directly accessible by my Struts Action classes.  For performance reasons, I know it is recommended to minimize the times you create the Hibernate Configuration and SessionFactory objects.

I did some Googling and didn't find much in the way of best practices for using Struts and Hibernate.  I figured it would make sense to create the Hibernate SessionFactory once and add it to the ServletContext so that it is available for the life of the application.  I found an example of this technique here and adapted it for my application.

I noticed the example created the SessionFactory and added it to the ServletContext but never closed it.  I added code to the contextDestroyed method of my ServletContextListener class to destroy the SessionFactory at shutdown.  I've run this by a couple of people and received differing opinions on whether it is necessary to close it but I don't think there is any harm in doing so.

Here's a snippet of the code I'm using:

public class HibernateListener implements ServletContextListener {

private Configuration config;
private SessionFactory sessionFactory;
private String path = "/hibernate.cfg.xml";

public static final String KEY_NAME = HibernateListener.class.getName();

@Override
public void contextDestroyed(ServletContextEvent arg0) {
    if ( sessionFactory != null ) {
        sessionFactory.close();
    }

}

@Override
public void contextInitialized(ServletContextEvent arg0) {
    try {
        URL url = HibernateListener.class.getResource(path);
        config = new Configuration().configure(url);
        sessionFactory = config.buildSessionFactory();

        // save the Hibernate session factory into serlvet context
        arg0.getServletContext().setAttribute(KEY_NAME, sessionFactory);
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
}
 Here's what I added to the web.xml
<listener>
    <listener-class>insert.package.name.here.HibernateListener</listener-class>
</listener>

Thursday, April 14, 2011

"Deployment Assembly" replaces "Java EE Module Dependencies" in Eclipse Helios

Frequent Eclipse users are likely already familiar with this UI change in Eclipse Helios but it's news to me and I figured I'd write a short post about it.

First, some quick background info: I spent some time last night getting up to speed on Struts 2.  I have a decent amount of experience using Struts but it's been a while.  In fact, it's been long enough that when I last used Struts, version 1.1 was the current version.  :) Anyway, I watched a couple of Struts videos on YouTube and read a few online tutorials (link1, link 2) and was ready to get started.  I won't go into detail in this post about the basic Struts configuration of Eclipse.

Everything was going according to plan until I added my previously created Eclipse User Library for Hibernate to the build path of the Dynamic Web Project.  The following warning appeared in the Markers view in Eclipse:
Classpath entry org.eclipse.jdt.USER_LIBRARY/Hibernate-3.6.2.Final will not be exported or published. Runtime ClassNotFoundExceptions may result.
Well, I guess that makes sense.  Thinking back to the days when I regularly used Eclipse (actually I was using the IBM branded version named Rational Application Developer), I figured I probably need to configure the project's "Java EE Module Dependencies".  The only problem was I didn't see it listed in the dialog for Project > Properties.  Huh?

I did some Googling and found some forum posts about the same problem.  It turns out the "Java EE Module Dependencies" has been replaced by the new "Deployment Assembly"  in Eclipse Helios (more specifically Web Tools Platform 3.2 as described here).

I added the Hibernate User Library to the deployment package structure by doing the following:
Project Properties > Deployment Assembly > Add > Java Build Path Entries > and selecting "Hibernate-3.6.2.Final" which is the name of my User Library.

I saved the configuration change and the Warning disappeared from the Markers view in Eclipse.

Wednesday, April 13, 2011

Hibernate - Part 4 - Testing persistence code with JUnit

I'd like to preface this post by saying this is not intended to be a best practice for testing Hibernate related application code.  I'd just like to show the methodology I'm using at the moment and discuss a few of the call outs I've found on this topic.  Of course, reader input and suggestions are always encouraged so please feel free to fire away.

In a previous post, I discussed the Hibernate mappings file and Ant tasks I'm using to create my Java entity classes and database tables.  I've since created some classes that wrap the CRUD functions for these entities.  I'll refer to these as my persistence classes. Of course these classes use the Hibernate framework (Transaction, Session, Query) and also have a little logic for dealing with the underlying relationship between the entities.

So now I need to test the code.  Yes I know, ideally I'd have written the tests before writing my persistence classes.  :)  I'm using JUnit for unit testing and since both JUnit and Hibernate are quite popular I figured I could quickly locate some online examples of using the two together.  Well, here's where things get a little complicated, or should I say controversial...

First off, I should probably refer to the testing of my persistence classes as integration testing and not unit testing.  As this Stack Overflow post explains, a unit test "should be a stand-alone test which is not related to other resources" and the integration test is "similar to unit tests but with external resources (db, disk access)".

Putting aside the semantics, what's the best way to use JUnit to test your Hibernate related code?  Here are some of the suggestions I've found:
  • Don't do a straight test of Hibernate related code with JUnit, use Spring instead.  Spring has a testing module for writing unit and integration tests.
  • Mock the Hibernate layer by using a mocking framework like JMock.  This enables you to focus your unit test on your code and removes any external frameworks (ie. Hibernate) from the test.
  • Use JUnit extension DbUnit to put your database into a known state before test runs.
  • Use an embedded, in-memory, database like HSQL for your testing.  This removes the reliance on your external full-fledged RDMS.
I'm eager to use Spring but it's not going to happen with this project or at least this phase of the project.  I'd also like to try the mocking or DbUnit approach, and I may do so but it won't happen until later either.

For reference, here's a couple of forum posts where JUnit and Hibernate are discussed:

For now, I'm going against better judgement and testing my persistence code with straight JUnit tests against my MySQL database.  I'll test the CRUD functions of my persistence classes and leave it at that.

I've created a JUnit test class for each of my persistence classes.  Rather than creating a Hibernate SessionFactory with each test method, I'll create it once in the JUnit @BeforeClass method and close it in the @AfterClass method.  Here's what it looks like along with one of the test methods:
    @BeforeClass
    public static void oneTimeSetup() throws Exception  {
        logger.debug("Entering oneTimeSetup()");
       
        // A SessionFactory is set up once for an application
        sessionFactory = new Configuration()
                .configure() // configures settings from hibernate.cfg.xml
                .buildSessionFactory();
    }
   
    @AfterClass
    public static void oneTimeTearDown() {
        logger.debug("Entering oneTimeTearDown()");
       
        if ( sessionFactory != null ) {
            sessionFactory.close();
        }
    }
   
    @Test
    public void testCreateTag() {
        logger.debug("Enter testCreateTag()");
       
        // create new Tag object
        Tag tag = new Tag("tag1");
       
        TagPersister tagPersister = new TagPersister();
       
        // Ask for a session using the JDBC information we've configured
        Session session = sessionFactory.openSession();
       
        assertTrue("Error while creating new Tag", tagPersister.createTag(tag, session));
    }
What do you think?  Reader ideas and suggestions are encouraged and appreciated.

    Tuesday, April 12, 2011

    Hibernate - Part 3 - Creating entity classes and database tables

    My previous blog post described the basic process I used to configure my Eclipse Java project to use Hibernate.  At the time, I was playing around with the example code from the Harnessing Hibernate book.  I found the examples helpful and now I'm ready to start using Hibernate in my own Java project.

    Hibernate gives you several options for creating your Java entity classes and database tables.  This is important and necessary because there will be times when we'll need to integrate Hibernate into existing projects where some combination of the entity classes and database tables may already exist.

    In my case, I'm literally starting from scratch and need to create everything.  Should I use Hibernate mapping files (.hbm), Hibernate Annotations, or Java Persistence API (JPA) Annotations?

    I played around with JPA Annotations as described in this tutorial video.  JPA has the advantage of being the Java standard and capable of working with ORM tools other than Hibernate.  I'd like to continue getting up to speed with JPA but for now I decided to use Hibernate mapping files (.hbm).

    I copied an existing .hbm file from the samples included in Harnessing Hibernate and modified it for my own entity classes.  After all, isn't copying existing code how we always get started?  :)

    At this point in my project, I have created an .hbm file for two entities: FlashCard and Tag.  What makes these interesting is that FlashCard contains a collection of Tag objects and will therefore need a join table (ie. flashcard_tag) in the database.  Here's where Hibernate shows its strength and ease of use.  We can define the relationship between these two entities in the mapping file and let Hibernate create the Java classes and database tables for us.  Hibernate understands the relationship.

    Here's an excerpt from a mapping file where the relationship between FlashCard and Tag is defined:
            <set name="tags" table="FLASHCARD_TAGS">
                <meta attribute="field-description">Tags for this FlashCard</meta>
                <key column="FLASHCARD_ID" />
                <many-to-many class="org.robbins.flashcards.model.Tag"
                    column="TAG_ID" />
            </set>
    Now that I've got my Hibernate mapping files ready, I need to have Hibernate go ahead and create my Java entity classes and tables in the MySQL database.

    I'm going to let Ant and the HibernateTool task generate the Java code and database tables.  Here's an excerpt from my build.xml which I adapted from the Harnessing Hibernate examples
        <!-- create the Java entity classes -->
        <target name="codegen" depends="prepare, refresh.local"
                    description="Generate Java source from the O/R mapping files">
            <hibernatetool destdir="${source.dir}">
                <configuration configurationfile="${source.dir}/hibernate.cfg.xml" />
                <hbm2java jdk5="true" />
            </hibernatetool>
        </target>

        <!-- create the database tables -->
        <target name="schema" depends="prepare, refresh.local"
                description="Generate DB schema from the O/R mapping files">

            <delete dir="${data.dir}" />
            <mkdir dir="${data.dir}" />

            <hibernatetool destdir="${source.dir}">
                <configuration configurationfile="${source.dir}/hibernate.cfg.xml" />
                <hbm2ddl drop="yes" />
            </hibernatetool>
        </target>

    Hibernate - Part 2- Getting Started

    As mentioned in a previous post, I've been preparing for using Hibernate in my latest project by reading Harnessing Hibernate, the Getting Started Guide, and watching some tutorials on YouTube.  At this point, I was ready to do some coding.

    I opted to download and play with some of the tutorials from the Harnessing Hibernate book.  However, I wouldn't be using the code as-is.  I'd be making some initial modifications to the sample code:
    1. I'd use the current version of Hibernate (v3.6.2.Final), Hibernate Tools (v3.2.4.GA) and Ant (v1.8.2) instead of the older versions discussed in the book.
    2. I decided, at least for now, not to use the Maven ant tasks for downloading and maintaining library dependencies.  Instead, I would manually download the Hibernate release bundles from SourceForge.
    3. I would run the examples through Eclipse rather than solely through the file system as described in the book.
    4. Instead of using the example "test" code as-is (Java classes with main() method), I would incorporate the test code into JUnit tests.
    So far so good, right?  Well I quickly ran into some challenges getting the sample code running on my dev system.

    Downloading and unpacking the Hibernate resource bundles is a no-brainer, but do I need to do anything special to Eclipse?  Of course, my project will need the Hibernate jars in its classpath (more on the later) but if I want the Eclipse IDE to include the Hibernate perspective and tooling I'll also need to install the Hibernate Tools directly into Eclipse.  Although I won't really leverage the Hibernate / Eclipse integration right now, I'd like to at least get it configured.  Rather than go step-by-step through the process, I recommend following the directions as described in the JBoss Tools 3.2 Installation From Update Site.

    At this point, I created a new Eclipse Java Project and imported the Harnessing Hibernate sample code for Chapter 3.  The sample code includes an Ant build file, the Hibernate configuration and mapping files as well as test classes for inserting records and querying records.

    I had compile errors in the Java classes that reference Hibernate.  It's worth noting that even though we installed the Hibernate Tools into Eclipse (and indeed Eclipse now recognizes and assigns special icons and editors to the Hibernate config and mapping files) we still need to add the Hibernate and dependent jars to the project build path.

    Several of the online tutorials I read suggested using Eclipse User Libraries as a convenient way to organize the Hibernate jars in the project's build path.  The User Libraries initially seemed like a good solution and my Java classes now compiled without errors.  However, I soon realized my Ant build wasn't aware of the User Libraries I created.  I describe in detail in an earlier blog post my attempts to overcome this issue.  In the end I decided to utilize User Libraries in Eclipse and add separate classpath references for the jars in the Ant build file.  It seems like a duplication of effort but it's workable for now.

    An inspection of the Ant build.xml included in the Harnessing Hibernate sample code shows Ant tasks for using the HibernateTool task to create both Java classes and Database tables.  The Hibernate Tool uses the hibernate.cfg.xml and a couple of Hibernate mapping files (.hbm) to determine what to create and where.  For these samples, we are using an HSQLDB database.

    Before we run the Ant tasks and actually use Hibernate for the first time, let's take a moment to review what we've done so far:
    1. Download and unpack the release bundles for Hibernate, Hibernate Took and Ant.
    2. Install the Hibernate Tools into Eclipse using Help > Install Software
    3. Create a new Eclipse Java Project and import the sample code from Harnessing Hibernate (Chapter 3)
    4. Create Eclipse User Libraries for Hibernate
    5. Update the Ant build.xml classpath with references to the Hibernate and Hibernate tool jars.
    At this point, I ran the HibernateTool tasks in the Ant build file for creating the Java entity classes and database tables.  Ant executed without any stack traces and ended with BUILD SUCCESSFUL.  Awesome!  I confirmed the new Java classes were created but when I fired up the HSQL Database Manager I couldn't see the new database table.  What gives?  Why did Ant finish successfully but not create the table?

    This caused a lot of confusion on my part and began a period of tweaking my classpath and log4j logging levels in an effort to understand the problem.  The first thing I noticed was my updated log4j level (now DEBUG) wasn't printing anything to the console.  I was only seeing INFO level logging.  Hmmm.

    Remember, up to now I've been running the Ant build directly from Eclipse.  At this point I tried running Ant directly from a Command Prompt and voila!  The output showed DEBUG log4j logging and most importantly the database table was now created in HSQL.

    After some more trial and error, I modified the JRE setting in Eclipse for the Ant build file (Right click on build.xml > Run As > Ant build...) and changed the Runtime JRE from "Run in the same JRE as the workspace" to "Separate JRE".  The JRE specified in the "Separate JRE" was the EXACT same JRE that Eclipse was using.

    I then reran Ant tasks from within Eclipse and this time it worked!  I don't understand why it works in a separate JRE but not within the same JRE.  I posted to the Tools forum in the Hibernate Community.  One of the guys from the Hibernate Team posted some responses but in the end suggested keeping the "Separate JRE" setting which is what I've done.  I also made a similar post on the Stack Overflow boards.

    Friday, April 8, 2011

    Using Eclipse "User Libraries" with Ant

    An Eclipse User Library is simply a set of JAR files defined within the IDE.  It provides a convenient way to organize and include jars in your project's build path without having to actually import the jars into your project and having them get deployed with your project.  User Libraries can be reused across multiple projects.  I haven't tried it myself, but it appears the User Libraries can be shared across machines with minimal hoop jumping.

    So far so good.  But how will Ant access these User Libraries?  As far as I can determine, there's no out-of-the-box way for Ant to reference an Eclipse's User Library in it's classpath.  Here's a couple of relevant forum posts on the topic:
    So what's the solution? Here's the options I've seen so far:
    1. Use the Eclipse User Library as-is then configure the Ant build file separately for the jars you need in your classpath.  I consider this option the "hard way" and of course it's what I'm doing for now.
    2. Use Apache Maven instead of Ant for build and project dependencies.  I'd like to give this a try when I have time. 
    3. I've seen a few people suggest using Ant4Eclipse.  I haven't had time to try it myself yet but it sounds like a potential solution.
    4. Don't utilize Eclipse User Libraries and copy your required jars to a /lib folder in your project.  Of course, this is just ignoring the issue isn't it. :)
    I'm interested in other approaches and options for this problem.  Please feel free to comment.  Thanks.

    Hibernate - Part 1- Learning Resources

    My previous experiences with enterprise Java utilized many of the core J2EE design patterns.  We used Data Access Objects (DAO), List Handlers, Value Objects, etc.  Sticking with these design patterns within our team ensured our apps would be flexible, reusable and standardized.

    Developers were able to jump from project to project and hit the ground running because the persistence code was similar between all of our applications.  I learned a lot from this experience and the J2EE design patterns provided good runtime performance

    The only problem I found with the J2EE design patterns for data persistence was the time required to create the necessary classes for each database table.  Coding the persistence layer was long and arduous and slowed down application development.

    In the years since we began using the core J2EE design patterns, open source frameworks have gained popularity. I'm eager to use Hibernate for the persistence layer in my new application.  I'm hopeful that Hibernate will reduce complexity and most importantly reduce the time required for persistence coding.

    I'm currently reading Harnessing Hibernate and working with some of the included code examples.  Some of the Amazon.com reviews are critical of this book's emphasis on other technologies like Ant and Maven.  The reviewers felt the book loses focus on Hibernate and spends too many pages elsewhere.  I don't share that sentiment, at least not yet.  I like to see how Hibernate fits into the whole ecosystem of a typical application.

    Here's a very basic step-by-step tutorial for setting up Hibernate and Log4j on Eclipse.

    I always look for YouTube videos on any new topic I'm trying to learn.  You might not think YouTube would be a great place to learn about java coding but there's plenty of helpful videos out there.  There's a series of 18 Hibernate videos from Patrick Washington that I've been watching and recommend.

    Here's a video of the 1st part in the18 part series:


    What resources have you found useful for learning Hibernate?  Your comments are welcome.