Re: Re: Tapestry-developer digest, Vol 1 #419 - 7 msgs
Eric Everman <[email protected]>
| Newsgroups | gmane.comp.java.tapestry.devel |
|---|---|
| Message-ID | <[email protected]> |
Roberto- Attached is my Hibernate helper bean and a related servlet. The servlet, HibTapServlet, replaces the normal Tapestry servlet and initializes a Hibernate SessionFactory based on a servlet init parameter and the hibernate.properties file. The SessionFactory is stored as a servlet context attribute so that it is accessible to the bean. The servlet also lets you specify the Tapestry .application file as an init parameter. The helper bean, SessionSource, has two public static methods that allow you to retrieve or rollback a Hibernate Session based on the passed Tapestry RequestCycle. Transactions are committed automatically and span a single page of a single request. Thus, if the application changes the page during the request and both pages use a session, they will each see a separate session. I should say that this has not been thoroughly tested and is simply "as far as I've gotten" in my experimentation with Tapestry and Hibernate. Please let me know if you run into any problems or have any suggestions on the implementation. Cheers, Eric Everman At 11/22/2002, Roberto Saccon wrote: >Hi Eric > >I am just switching from EJB to Hibernate and I am interested to see how you >implemented that bean and would appreciate if you post that code. >My current very, very simple approach requires for every DB-access a new >Hibernate-session (they are light-weight) and then immediatly commits it. > >regards >Roberto Saccon
HibTapServlet.java
(text/plain, 4.3 KB)
package com.preceda.hibertap;
import java.io.InputStream;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import net.sf.tapestry.ApplicationServlet;
import cirrus.hibernate.Datastore;
import cirrus.hibernate.Hibernate;
import cirrus.hibernate.Session;
import cirrus.hibernate.SessionFactory;
/**
* @author Eric Everman
*
* A Hibernate specific subclass of the Tapestry ApplicationServlet
*
* This Servlet adds two features to the basic Tapestry Servlet. First,
* it allows the Tapestry .application file location to be specified as
* an init parameter of the servlet. Second, it configures a Hibernate
* SessionFactory based on an init parameter and the hibernate.properties
* file.
*
* See the three constants for info on how to configure.
*/
public class HibTapServlet extends ApplicationServlet {
/**
* The next to Init value names for use in web.xml file
*/
/**
* Name of a servlet init-param that must be specified in the web.xml file
*
* A context relative location of the Tapestry .application file
* in the form "/com/corp/myapp.application"
*/
private static final String APP_SPEC_PATH_PARAM_NAME = "Application_Specification_Path";
/**
* Name of a servlet init-param that must be specified in the web.xml file
*
* A aist of fully qualified class names that Hibernate should persist.
* Names can be separated with commas, semi-colons, or spaces.
* For each class persisted, Hibernate expects to find a file named
* "ClassName.hbm.xml" in the same package location as the class.
*/
private static final String HIBERNATE_CLASS_NAMES = "Hibernate_Class_Names";
/**
* Name of the Hibernate Properties file, which must be placed in the
* classpath root. For use with the SessionSource, the properties file should
* configure Hibernate to provide its own connections.
*/
private static final String HIBERNATE_PROPERTIES = "hibernate.properties";
private String appSpecPath; //The Tapestry .application file path
public void init(ServletConfig config) throws javax.servlet.ServletException {
//Assign Tapestry application file
appSpecPath = config.getInitParameter(APP_SPEC_PATH_PARAM_NAME);
if (appSpecPath == null || appSpecPath.length() == 0) {
throw new javax.servlet.ServletException(
"Required init param not found or empty: " + APP_SPEC_PATH_PARAM_NAME);
}
//Build persistant class list for Hibernate.
//Valid delimiters are ,; and [space].
String hibClasses = config.getInitParameter(HIBERNATE_CLASS_NAMES);
if (hibClasses == null || hibClasses.length() == 0) {
throw new javax.servlet.ServletException(
"Required init param not found or empty: " + HIBERNATE_CLASS_NAMES);
} else {
StringTokenizer st = new StringTokenizer(hibClasses, ",; ");
Datastore ds = Hibernate.createDatastore();
try {
while (st.hasMoreElements()) {
ds.storeClass(Class.forName(st.nextToken()));
}
} catch (Exception e) {
throw new ServletException(
"An error occured while building Hibernate's persistant class list: " +
e.getClass().getName() + " " + e.getMessage());
}
//Load Hibernate Properties from properties file
Properties props = new Properties();
InputStream iStream;
SessionFactory sf;
try {
// "/" causes the loader to not convert dots to slashes (ie abs path).
iStream = this.getClass().getResourceAsStream("/" + HIBERNATE_PROPERTIES);
props.load(iStream);
//Build session factory
sf = ds.buildSessionFactory(props);
//Test session
Session s = sf.openSession();
s.beginTransaction();
s.connection().commit();
s.close();
//Place factory in application context
config.getServletContext().setAttribute(SessionSource.FACTORY_KEY, sf);
iStream.close();
} catch (Exception e) {
ServletException se = new ServletException(
"An error occured while creating a Hibernate Session Factory: " +
e.getClass().getName() + " " + e.getMessage());
se.setStackTrace(e.getStackTrace());
throw se;
}
}
super.init(config);
}
/**
* Return the Tapestry .application path
*/
protected String getApplicationSpecificationPath() throws javax.servlet.ServletException {
return appSpecPath;
}
}
SessionSource.java
(text/plain, 6 KB)
package com.preceda.hibertap;
/**
* @author Eric Everman
*
* Provides a means to retrieve Hibernate Sessions within Tapestry.
*
* <p>Any page or component can access a Hibernate Session by calling
* <code>SessionSource.getSession(IRequestCycle)</code>
* If no Session has been created for the current page of the passed
* RequestCycle, a new Session will be created and stored as an attribute
* of the cycle. Subsequent calls to <code>getSession()</code> during a
* request cycle will (mostly - see below) return the existing Session.
*
* SessionSource creates a Hibernate Session that spans a SINGLE PAGE of
* a request cycle. If the page changes during the cycle via
* <code>cycle.setPage()<code>, the current Session's transaction is
* committed and the Session is discarded. Further requests for a Session
* will result in a new Session with a transaction that spans the new page,
* and so on.
*
* SessionSource uses the PageEvent pageEndRender to determine when it should
* commit its transaction and free the Session. Under normal circumstances, you
* do not need to worry about committing transactions or flushing the session.
* To rollback a transaction, you can call <code>rollbackAndClose()</code>.
*
* NOTE: This helper bean assumes that a Hibernate SessionFactory is stored
* as an attribute of the ServletContext with the the name <code>FACTORY_KEY</code>.
* This factory must be configured to supply its own connections.
*/
import net.sf.tapestry.IPage;
import net.sf.tapestry.IRequestCycle;
import net.sf.tapestry.event.PageEvent;
import net.sf.tapestry.event.PageRenderListener;
import cirrus.hibernate.Session;
import cirrus.hibernate.SessionFactory;
import cirrus.hibernate.Transaction;
public class SessionSource {
public final static String SESSION_KEY = "DEFAULT_HIBERNATE_SESSION";
public final static String FACTORY_KEY = "DEFAULT_HIBERNATE_FACTORY";
static final PageRenderListener pageRenderListener;
//Initilize a single PRL for use on all pages.
static {
pageRenderListener = new PageRenderListener() {
public void pageBeginRender(PageEvent event) { /*Don't need this event*/ }
public void pageEndRender(PageEvent event) {
//System.out.print("*** PageEndRender Event");
closeSession(
(SessionWrapper)event.getRequestCycle().getAttribute(SESSION_KEY));
event.getRequestCycle().removeAttribute(SESSION_KEY);
event.getPage().removePageRenderListener(pageRenderListener);
}
};
}
/**
* Retrieves an existing Hibernate Session or creates a new one.
*/
public static Session getSession(IRequestCycle cycle) throws Exception {
SessionWrapper sw = (SessionWrapper)cycle.getAttribute(SESSION_KEY);
Session sess = null;
//System.out.print("*** getSession");
if (sw != null) sess = sw.session;
if (sess == null) {
//No previous session, create new
sess = createSession(cycle);
} else if (! sess.isOpen()) {
//The user closed the session - do some cleanup
cycle.removeAttribute(SESSION_KEY);
cycle.getPage().removePageRenderListener(pageRenderListener);
//Now create a new session
sess = createSession(cycle);
} else if (! sess.isConnected()) {
//The user disconnected the session
sess.reconnect();
}
return sess;
}
/**
* Rolls back the session's transaction and closes the session.
*
* After calling this method, the current session is defunct and cannot
* be reused.
*/
public static void rollbackAndClose(IRequestCycle cycle) {
SessionWrapper sw = (SessionWrapper)cycle.getAttribute(SESSION_KEY);
Session sess = sw.session;
Transaction trans = sw.transaction;
//System.out.print("*** Rollback");
if (sw != null) {
sess = sw.session;
trans = sw.transaction;
}
if (trans != null) {
try {
trans.rollback();
} catch (Exception e) { /* Not much we can do */ }
try {
sess.close();
} catch (Exception e) { /* Not much we can do */ }
}
//Cleanup
cycle.removeAttribute(SESSION_KEY);
cycle.getPage().removePageRenderListener(pageRenderListener);
}
/**
* Create a new session and store it as an attribute of the RequestCycle.
*
* This method also adds a pageEndRender listener on the current page so
* that the session can be automatically committed when the page has
* completed rendering.
*/
private static Session createSession(IRequestCycle cycle) throws Exception {
//System.out.print("*** Create Session");
//Create new session
SessionFactory sf = (SessionFactory)( cycle.getRequestContext()
.getServlet().getServletContext().getAttribute(FACTORY_KEY) );
Session session = sf.openSession();
Transaction trans = session.beginTransaction();
SessionWrapper sw = new SessionWrapper();
sw.session = session;
sw.transaction = trans;
cycle.setAttribute(SESSION_KEY, sw);
//Add pageRenderListeneer
IPage page = cycle.getPage();
page.addPageRenderListener(pageRenderListener);
return session;
}
/**
* Close and commit the session contained in the passed sessionWrapper
*/
private static void closeSession(SessionWrapper sw) {
//System.out.print("*** Close Session");
if (sw != null) {
Session session = sw.session;
Transaction trans = sw.transaction;
if (session != null && session.isOpen()) {
try {
try {
trans.commit();
//System.out.print("*** Session Closed OK");
} catch (Exception ee) {
//System.out.print("*** Error during session close");
trans.rollback();
} finally {
session.close();
}
} catch (Exception e) {
//Can't do anything at this point - rollback or close failed
}
} else {
//System.out.print("*** Session is closed (!!)");
}
} else {
//System.out.print("*** There is no SessionWrapper!!!");
}
}
/**
* Inner class used to encapsilate a session with its transaction
*/
static class SessionWrapper {
protected Session session;
protected Transaction transaction;
}
}