Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0

TODO: Documenting updated version (currently at http://www.i-tao.com/adminapp.htmlImage Removed). IN PROGRESS.

...

  • /lib: contains the various jars for our application. Nothing special here.
  • /src/java/org/hibernate/admin/action: lists our WebWork actions. All actions extend an abstract AbstractAction file, which overrides the execute() method from our XWork's ActionSupport. This is where we define a setHibernateSession() method, which is the method we declared in our enabler interface (HibernateSessionAware). This will notify XWork to invoke its IoC magic to set the HibernateSession.
Code Block
javajava
titleorg.hibernate.admin.action.AbstractAction
java
        public String execute() throws Exception {
	
		// We go to INPUT on field and data errors
		if ( hasErrors() ) {
			LOG.debug("action not executed, field or action errors");
			LOG.debug( "Field errors: " + getFieldErrors() );
			LOG.debug( "Action errors: " + getActionErrors() );
			return INPUT;
		}

		LOG.debug("executing action");
		return go();
	}
	
	protected abstract String go() throws HibernateException;

	public void setHibernateSession(HibernateSession session) {
		this.session = session;
	}

	protected Session getSession() throws HibernateException {
		return session.getSession();
	}

In this execute() method we'll simply call a abstract go() method (which is then defined in each of the actions). When we need the Hibernate Session, we use the getSession() method, inherited from our AbstractAction. Don't worry about transactions or saving so called dirty objects (our HibernateInterceptor takes care of all that). As you can see, this totally minimizes the LoC (lines of code) needed to retrieve or manipulated our models).

java
Code Block
java
titleorg.hibernate.admin.action.EditUserAction
java
public class EditUserAction extends AbstractAction {
	//.. ommited for brevity
	
	protected String go() throws HibernateException {
		..
		getSession().update(user);
		..
		return SUCCESS;
	}
	
	//.. getters and setters ommited

}

There are 3 more *-validation.xml files in this directory containing the validation logic for the Actions. XWork will validate your request before the action gets executed, so you can decouple your (simple) validation logic from your Action. For example, take a look at the CreateUserAction-validation.xml:

Code Block
xmlxml
titleCreateUserAction-validation.xml
xml
..
    <field name="user.name.lastName">
        <field-validator type="requiredstring">
            <message>You must enter a last name.</message>
        </field-validator>
    </field>
    <field name="user.email">
        <field-validator type="email">
            <message>Please correct the e-mail address.</message>
        </field-validator>
        <field-validator type="required">
            <message>Please enter an e-mail address.</message>
        </field-validator>
    </field>
..

...

a) actually provided an input type in your struts.xml file

Code Block
xmlxml
titlexwork.xml
xml
	..
        <result name="input" type="dispatcher">
		<param name="location">/editUser.jsp</param>
	</result>
	..

b) you enabled the validation interceptor in your struts.xml

xml
Code Block
xml
titlexwork.xml
xml
	..
	<interceptor-ref name="defaultStack"/>
	<interceptor-ref name="validation"/>
	..

c) you use the WebWork tag library (warning: this is the old syntax):

Code Block
xmlxml
titleCreateUser.jsp
xml
..
<ww:form name="'createUserForm'" action="'createUser.action'" method="'POST'">
    <ww:textfield label="'Username'" name="'user.handle'"/>
..

New syntax (since 2.2):

xml
Code Block
xml
titleCreateUser.jsp
xml
..
<ww:form name="createUserForm" action="createUser" method="POST">
    <ww:textfield label="Username" name="user.handle"/>
..
  • /src/java/org/hibernate/admin/component: contains the components and enablers for both the HibernateSessionFactory and the HibernateSession. These components are declared in the /src/java/components.xml file (which will be copied to the root of your compiled classes afterwards):
Code Block
xmlxml
titlecomponents.xml
xml
<components>

    <component>
        <scope>request</scope>
        <class>org.hibernate.admin.component.HibernateSession</class>
        <enabler>org.hibernate.admin.component.HibernateSessionAware</enabler>
    </component>

    <component>
        <scope>application</scope>
        <class>org.hibernate.admin.component.HibernateSessionFactory</class>
        <enabler>org.hibernate.admin.component.HibernateSessionFactoryAware</enabler>
    </component>

</components>
  • /src/java/org/hibernate/admin/interceptor: contains the Hibernate interceptor. Interceptors are an incredibly powerful feature of WebWork - it allows you to control invocations before and after they excute, manipulate their results, or, as in our case, extract the HibernateSession object and dispose it after the Action has been executed (and the view rendered). Because we use a try/catch/finally block, we're able to catch exceptions and yet make sure our Session gets closed properly (the number one cause of db connection leaks).
Code Block
javajava
titleorg.hibernate.admin.interceptor.HibernateInterceptor
java
	public String intercept(ActionInvocation invocation) throws Exception {
		Action action = invocation.getAction();
		if ( !(action instanceof AbstractAction) ) return invocation.invoke();
		
		HibernateSession hs = ( (AbstractAction) action ).getHibernateSession();
		try {
			return invocation.invoke();
		}
		
		// Note that all the cleanup is done
		// after the view is rendered, so we
		// have an open session in the view
		
		catch (Exception e) {	
			hs.setRollBackOnly(true);
			if (e instanceof HibernateException) {
				LOG.error("HibernateException in execute()", e);
				return Action.ERROR;
			}
			else {
				LOG.error("Exception in execute()", e);
				throw e;
			}
		}
		
		finally {
			try {
				hs.disposeSession();
			}
			catch (HibernateException e) {
				LOG.error("HibernateException in dispose()", e);
				return Action.ERROR;
			}
		}
	}

...