Archive for the 'JavaEE' Category

22
May
11

JSF Datatable with database sorting, filtering and pagination

When database grows (or may grow) significantly it is necessary to consider database pagination for loading data since the Application Server memory isn’t sized for handling such huge amount of data.

Luckily rich:extendedDataTable provides an easy way of delegating the sorting, filtering and also pagination to the underlying data store. I sincerely remember that the I saw the base for this idea somewhere in Seam Framework forum (if anyone knows something similar please let me know) but I couldn’t find it for referencing, anyways it is now extended and more flexible. Another foundation of this technique is the org.jboss.seam.framework.Query since it provides means of expressing filtering and sorting as a huge string even with JSF variables (eg.: #{something.otherthing}).

Lets start with the EntityExtendedTableDataModel that is responsible for passing filtering and sorting data from view layer to data layer:

<pre>/**
EntityExtendedTableDataModel - Database pagination, sorting and filtering for richfaces datatables
Copyright (C) 2011 Rafael Ribeiro

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/</pre>
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.faces.component.html.HtmlInputText;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;

import org.apache.commons.lang.StringUtils;
import org.jboss.seam.core.Expressions;
import org.jboss.seam.core.Expressions.ValueExpression;
import org.jboss.seam.framework.Query;
import org.jboss.seam.ui.AbstractEntityLoader;
import org.richfaces.model.DataProvider;
import org.richfaces.model.ExtendedFilterField;
import org.richfaces.model.ExtendedTableDataModel;
import org.richfaces.model.FilterField;
import org.richfaces.model.Modifiable;
import org.richfaces.model.Ordering;
import org.richfaces.model.SortField2;

public class EntityExtendedTableDataModel extends ExtendedTableDataModel implements Modifiable {
EntityDataProvider entityDataProvider;
public EntityExtendedTableDataModel(Query query) {
super(new EntityDataProvider(query));
entityDataProvider = (EntityDataProvider) getDataProvider();
}

public void modify(List<FilterField> filterFields,
List<SortField2> sortFields) {
performSort(sortFields);
performFilter(filterFields);
}

private void performSort(List<SortField2> sortFields) {
StringBuilder order = new StringBuilder();
Pattern p = Pattern.compile("\\#\\{(.+)\\}");
for (SortField2 s: sortFields) {
if (Ordering.UNSORTED.equals(s.getOrdering()) == false) {
String expr = s.getExpression().getExpressionString();
Matcher m = p.matcher(expr);
if (m.matches()) { //remove the #{} otherwise richfaces wont trigger the sort event
order.append(m.group(1));
}
else {
order.append(expr);
}
order.append(" ");
order.append(Ordering.ASCENDING.equals(s.getOrdering()) ? "ASC" : "DESC");
order.append(", ");
}
}
if (order.length() > 0)
entityDataProvider.getQuery().setOrder(order.delete(order.length()-2, order.length()).toString());
}
//allows us to specify filter as o.name == #{exampleEntity.name}
private void performFilter(List<FilterField> filterFields) {
Expressions expressions = new org.jboss.seam.core.Expressions();
Pattern p = Pattern.compile(".*(\\#\\{.+\\}).*");
List<String> restrictions = new ArrayList<String>();
for (FilterField f: filterFields) {
ExtendedFilterField e = (ExtendedFilterField) f;
if (StringUtils.isEmpty(e.getFilterValue()))
continue;
StringBuilder filter = new StringBuilder();
String expr = e.getExpression().getExpressionString();
Matcher m = p.matcher(expr);
if (!m.matches())
continue;
ValueExpression ve = expressions.createValueExpression(m.group(1));
FacesContext ctx = FacesContext.getCurrentInstance();
Converter c = ctx.getApplication().createConverter(ve.getType());
if (c == null)
ve.setValue(e.getFilterValue());
else {
try {
ve.setValue(c.getAsObject(ctx, new HtmlInputText(), e.getFilterValue()));
} catch (ConverterException ce) {
continue;
}
}
restrictions.add(expr);
}
entityDataProvider.getQuery().setRestrictionExpressionStrings(restrictions);
}

}
class EntityDataProvider implements DataProvider {

private Query query;
public EntityDataProvider(Query query) {
this.query = query;
}

public Object getItemByKey(Object key) {
return AbstractEntityLoader.instance().get(String.valueOf(key));
}

public List getItemsByRange(int firstRow, int endRow) {
query.setFirstResult(firstRow);
query.setMaxResults(endRow-firstRow);
return query.getResultList();
}

public Object getKey(Object item) {
return AbstractEntityLoader.instance().put(item);
}

public int getRowCount() {
return query.getResultCount().intValue();
}
protected Query getQuery() {
return query;
}
}

Now we have to replace Seam datamodels component with ours:

<pre>EntityExtendedTableDataModel - Database pagination, sorting and filtering for richfaces datatables
Copyright (C) 2011 Rafael Ribeiro

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/</pre>
import static org.jboss.seam.ScopeType.STATELESS;
import static org.jboss.seam.annotations.Install.FRAMEWORK;

import javax.faces.model.DataModel;

import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.faces.DataModels;
import org.jboss.seam.framework.EntityQuery;
import org.jboss.seam.framework.Query;

@Name("org.jboss.seam.faces.dataModels")
@Install(precedence=FRAMEWORK)
@Scope(STATELESS)
@BypassInterceptors
public class RichFacesModels extends DataModels
{

@Override
public DataModel getDataModel(Query query)
{
if (query instanceof EntityQuery)
{
return new EntityExtendedTableDataModel((EntityQuery) query);
}
else
{
return super.getDataModel(query);
}
}

}

Finally we specify in rich:column the filtering remembering that the base object name here must be in sync with the referenced query, example:


<rich:extendedDataTable id="listaPacientes" rows="10" value="#{queryAllEntities.dataModel}" var="obj">
<rich:column sortable="true" sortBy="#{o.name}" selfSorted="false"
filterBy="lower(o.name) like concat(lower(#{exampleEntity.name}),'%')" filterEvent="onkeyup"
label="Name">

The trick here is to specify selfSorted=”false” and adding the JSF #{} part for the sorting to work, this way o.name will be appended to Query sorting.

On the other hand, for filtering to work you only need to specify your query, note that what you specify between the #{} will hold the filter data handed to the Query.

Also note that you need to have a query and an entity as a component specified in components.xml, in this example, this could be the query and the example entity:


<component name="exampleEntity" class="br.com.rafaelri.MyEntity" scope="session" />
<framework:entity-query name="queryAllEntities" ejbql="select o from MyEntity o" />

This, combined with rich:dataScroller will result in sorting, pagination and filtering handled by the Database.

21
May
11

Enum backed h:selectOneRadio and h:selectOneMenu

Recently I needed to display a h:selectOneRadio and a h:selectOneMenu with values provided by a Java Enum. After some research on seamframework forum I saw one post that clarified a lot the way I should pursue, specially Pete’s comment. My greatest concern was the same of Pete: wiring up presentation and domain layer. But, after some analysis I found a simple solution that does not wire up domain and presentation layer and also respects the DRY principle. The solution involved crafting a class that would be instantiated as a Seam component on components.xml so it can be instantiated for each and every enum that we want to provide on view layer and also used EnumSet.allOf method so we could automagically iterate for each of the enum values.

Below, you can find the EnumList class:

/**
EnumList Seam Component - Converts Java Enums to List.
Copyright (C) 2011 Rafael Ribeiro

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

public class EnumList<T extends Enum<T>> {
private List<T> list;

public void setEnumClass(Class<T> c) {
list = new ArrayList<T>();
list.addAll(EnumSet.allOf(c));
}

public List<T> getList() {
return list;
}

public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("list", list).toString();

}
}

And the configure it on components.xml as follows:

<component name="myEnumComp" scope="application" auto-create="true">
<property name="enumClass">br.com.rafaelri.MyEnum</property>
</component>
<factory name="myEnum" value="#{myEnumComp.list}" scope="application" auto-create="true"/>

Finally you’ll refer to it on your xhtml as follows:

<h:selectOneMenu id="myEnumSelect" value="#{instanceHome.instance.myEnum}">
<s:selectItems var="enum" value="#{myEnum}" label="#{enum.description()}"/>
<s:convertEnum />
</h:selectOneMenu>


21
May
11

JPA 2 on Seam 2

As intelligently pointed by Thomas on CTP Java blog starting a Java Web application today from scratch presents you an interesting crossroads. crossroadsIf you stick with the proven Seam 2 option you end up also with its deficiencies not to mention some old versioned libraries. The other option is experimenting with Seam 3 but this on the other hand has few knowledge critical mass around. But with a few changes, Seam 2 can still provide value and at least Hibernate/JPA version can be improved bringing a bunch of bugfixes (eg.: proper schema generation that in Hibernate 3.3 was buggy – even though HHH1012 says it is fixed I never saw this on any 3.3 release) and new features (eg.: criteria api as also pointed by Thomas).

I’ve followed Thomas instructions but instead of coding the proxy method by method I suggest using Java Dynamic Proxies:


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import javax.persistence.EntityManager;

import org.jboss.seam.persistence.PersistenceProvider;

public class Jpa2EntityManagerProxy implements InvocationHandler {

private EntityManager delegate;

public Jpa2EntityManagerProxy(EntityManager delegate) {
super();
this.delegate = delegate;
}

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.getName().equals("getDelegate")) {
return PersistenceProvider.instance().proxyDelegate(
delegate.getDelegate());
} else {
try {
return method.invoke(delegate, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
}

public static EntityManager newInstance(EntityManager entityManager) {
return (EntityManager) Proxy.newProxyInstance(
Jpa2EntityManagerProxy.class.getClassLoader(),
new Class[] { EntityManager.class },
new Jpa2EntityManagerProxy(entityManager));
}
}

Apart from this change I followed Thomas instructions as-is and it got JPA2 with Hibernate 3.5 working in my project.

15
Jul
10

JBoss Seam application blueprint

I am a JBoss Seam user since its v2.0 alpha something (back in 2007). I still remember the hard decision in picking up the alpha and later beta version instead of sticking with the stable but feature missing 1.2.
Seam is an incredible framework for web applications, it covers the majority of the requirements you have in such applications. But this tremendous power comes with a price, it is often hard to find the best combination in the first application you develop. Its variety of contexts combined with the possibilities of handling the page data through injection and outjection results in a challenge for the Seam newbie, not to mention the possibilities of handling the flow between pages…

That’s the reason I thought about developing a blueprint for Seam applications… I know that for now SeamGen generated applications are considered blueprints for Seam applications but I really feel like there are plenty of missing parts. SeamGen applications don’t use Conversation scopes (only to give an example cause the list of Seam features that are not explored by a SeamGen application are enormous). I am also sure that I won’t develop THE blueprint for a Seam application but at least I’ll try to document all the knowledge I’ve gathered from a few projects and a few POCs.

Managing page flow and conversation demarcation

Seam has a neat feature for specifying page flow: pages.xml and view-id.page.xml files. The first one is able to specify navigation rules for every view in the application and should be used for specifying global rules as when Exceptions are thrown or for actions that have the same result independent of the current view. This post presents good practices for defining page flows using Seam. There is even one thing covered in this post that I recommend: specifying conversation demarcation on view-id.page.xml files but there is one thing suggested on this post that I need to investigate carefully: the impact of joining conversations instead of spawning new ones. I agree with the post that not joining may spawn unwanted conversations and thus increase memory usage but I cant say beforehand which are the drawbacks of joining a conversation.
A good way of redirecting the user to a new view with total control over conversation propagation is by using the s:button tag. This tag has one property named view that specifies the target view-id and another one called propagation in which you can specify the conversation propagation. The following example redirects to a view named “newUser.xhtml” and suspending the current conversation scope:

<s:button value="New User" view="/newUSer.xhtml" propagation="none"/>

And if this view required a conversation scope this could be specified on newUser.page.xml with the <begin-conversation> tag.

Authentication and authorization

Everytime that you are developing an application targeting a deploy on a full fledged J2EE application server and if possible, prefer to delegate authentication and authorization to the Application Server JAAS. The following code when configured on Seam components.xml delegates to the specified JAAS domain:

<security:identity jaas-config-name="myJAASDomainName" remember-me="true"/>

This avoids the rather limited approach of specifying an authenticator method on an application Seam component since a JAAS authenticated user will be propagated all the way down the EJB container (in case you are using it).

That’s all for now I’ll try to update this post as soon as I format more knowledge around JBoss Seam.

27
Jun
10

Hibernate Isolater.doIsolatedWork WebSphere compliant implementation based on UOWManager.runUnderUOW

Recently I decided to deepen my investigation upon Spring WebSphereUowTransactionManager. I always thought it was a shame Hibernate wasn’t fully supported under WebSphere and decided to investigate it a little more since I had the impression that after my first investigation that resulted in my first attempt of extending current hibernate transaction lookup for WebSphere it would take only a little more effort.

Considerations for the Implementation

The first decision taken for the implementation was that it should have no compile-time dependency upon WebSphere as Hibernate original TransactionManager and TransactionManagerLookup. This resulted in having all the calls to UOWManager and UOWSynchronizationRegistry using reflection and hence we needed to pass a UOWAction instance to UOWManager.runUnderUOW I chose to use a Java Dynamic Proxy.

Changes to Hibernate and Implementation

Since I didn’t want to embed another inner class into Isolater.java I made the Delegate interface public instead of private. The last trick was to return my delegate in place of JtaDelegate when configured for WebSphere. I sincerely feel like a factory was a better option over here but I didn’t take any time to investigate where to plug it. The resulting code for Isolater is show below:

	public static void doIsolatedWork(IsolatedWork work, SessionImplementor session) throws HibernateException {
		boolean isJta = session.getFactory().getTransactionManager() != null;
		if ( isJta ) {
			if (session.getFactory().getTransactionManager() instanceof UOWTransactionManagerAdapter)
				new WebSphereUOWDelegate(session).delegateWork(work, true);
			else
				new JtaDelegate( session ).delegateWork( work, true );
		}
		else {
			new JdbcDelegate( session ).delegateWork( work, true );
		}
	}

	public static void doNonTransactedWork(IsolatedWork work, SessionImplementor session) throws HibernateException {
		boolean isJta = session.getFactory().getTransactionManager() != null;
		if ( isJta ) {
			if (session.getFactory().getTransactionManager() instanceof UOWTransactionManagerAdapter)
				new WebSphereUOWDelegate(session).delegateWork(work, false);
			else
				new JtaDelegate( session ).delegateWork( work, false );
		}
		else {
			new JdbcDelegate( session ).delegateWork( work, false );
		}
	}

	public static interface Delegate {
		public void delegateWork(IsolatedWork work, boolean transacted) throws HibernateException;
	}

The resulting Isolater.Delegate implementation for WebSphere delegates the doTheWork to a method on UOWTransactionManagerAdapter that creates the UOWAction proxy and carries it on to UOWManager.

	private void doTheWork(IsolatedWork work, UOWTransactionManagerAdapter transactionManager, boolean transacted) {
		try {
			// obtain our isolated connection
			Connection connection = session.getFactory().getConnectionProvider().getConnection();
			try {
				// do the actual work
				if (transacted)
					transactionManager.doIsolatedWork(work, connection, false, true);
				else
					transactionManager.doIsolatedWork(work, connection, false, false);
			}
			catch ( HibernateException e ) {
				throw e;
			}
			catch ( Exception e ) {
				throw new HibernateException( "Unable to perform isolated work", e );
			}
			finally {
				try {
					// no matter what, release the connection (handle)
					session.getFactory().getConnectionProvider().closeConnection( connection );
				}
				catch ( Throwable ignore ) {
					log.info( "Unable to release isolated connection [" + ignore + "]" );
				}
			}
		}
		catch ( SQLException sqle ) {
			throw JDBCExceptionHelper.convert(
					sqlExceptionConverter(),
					sqle,
					"unable to obtain isolated JDBC connection"
			);
		}
	}

A class implementing InvocationHandler is responsible for delegating the UOWAction implementation to the Hibernate IsolatedWork

static class UOWInvocationHandler implements InvocationHandler {

private IsolatedWork work;
private Connection connection;

public UOWInvocationHandler(IsolatedWork work, Connection connection) {
super();
this.work = work;
this.connection = connection;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
work.doWork(connection);
return void.class;
}

}

Finally we have the doIsolatedWork method on UOWTransactionManagerAdapter

		public void doIsolatedWork(IsolatedWork work, Connection conn, boolean join, boolean global) throws SystemException, InvalidTransactionException, IllegalStateException {
			Object p = Proxy.newProxyInstance
	          (uowActionClass.getClassLoader(),
	                new Class[] { uowActionClass }, new UOWInvocationHandler(work, conn));
			try {
				runUnderUOWMethod.invoke(uowManager, new Object[]{global ? UOW_TYPE_GLOBAL_TRANSACTION : UOW_TYPE_LOCAL_TRANSACTION, Boolean.FALSE.booleanValue(),p});
			} catch (IllegalArgumentException e) {
				throw new HibernateException(e);
			} catch (IllegalAccessException e) {
				throw new HibernateException(e);
			} catch (InvocationTargetException e) {
				throw (HibernateException) e.getTargetException().getCause();
			}
		}

I still need to develop a test application for the Isolater.doNonTransactedWork method… if anyone knows how Hibernate triggers that flow and is willing to give a try, please let me know on the comments.
With a patched Hibernate you only need to configure the hibernate.cfg.xml with the following transaction.manager_lookup_class:

		   <property name="hibernate.transaction.manager_lookup_class">
		       org.hibernate.transaction.WebSphereUOWTransactionLookup
           </property>

I am providing a Diff for anyone willing to patch Hibernate and testing this implementation.

diff -r -d -N src/org/hibernate/engine/transaction/Isolater.java ..\..\hib-orig\src/org/hibernate/engine/transaction/Isolater.java
40d39
< import org.hibernate.transaction.WebSphereUOWTransactionLookup.UOWTransactionManagerAdapter;
68,71c67
< 			if (session.getFactory().getTransactionManager() instanceof UOWTransactionManagerAdapter)
< 				new WebSphereUOWDelegate(session).delegateWork(work, true);
< 			else
< 				new JtaDelegate( session ).delegateWork( work, true );
---
> 			new JtaDelegate( session ).delegateWork( work, true );
89,92c85
< 			if (session.getFactory().getTransactionManager() instanceof UOWTransactionManagerAdapter)
< 				new WebSphereUOWDelegate(session).delegateWork(work, false);
< 			else
< 				new JtaDelegate( session ).delegateWork( work, false );
---
> 			new JtaDelegate( session ).delegateWork( work, false );
103c96
< 	public static interface Delegate {
---
> 	private static interface Delegate {
diff -r -d -N src/org/hibernate/engine/transaction/WebSphereUOWDelegate.java ..\..\hib-orig\src/org/hibernate/engine/transaction/WebSphereUOWDelegate.java
1,89d0
< package org.hibernate.engine.transaction;
<
< import java.sql.Connection;
< import java.sql.SQLException;
<
< import org.hibernate.HibernateException;
< import org.hibernate.engine.SessionImplementor;
< import org.hibernate.engine.transaction.Isolater.Delegate;
< import org.hibernate.exception.JDBCExceptionHelper;
< import org.hibernate.exception.SQLExceptionConverter;
< import org.hibernate.transaction.WebSphereUOWTransactionLookup.UOWTransactionManagerAdapter;
< import org.slf4j.Logger;
< import org.slf4j.LoggerFactory;
<
< /**
<  * An isolation delegate for WebSphere UOW-based transactions.  Essentially calls UOW for
<  * suspending any current transaction, does the work in a new transaction, and then
<  * resumes the initial transaction (if there was one).
<  */
< public class WebSphereUOWDelegate implements Delegate {
< 	private static final Logger log = LoggerFactory.getLogger( Isolater.class );
<
< 	private final SessionImplementor session;
<
< 	public WebSphereUOWDelegate(SessionImplementor session) {
< 		this.session = session;
< 	}
<
< 	public void delegateWork(IsolatedWork work, boolean transacted) throws HibernateException {
< 		UOWTransactionManagerAdapter transactionManager = (UOWTransactionManagerAdapter) session.getFactory().getTransactionManager();
<
< 		// then peform the requested work
< 		if ( transacted ) {
< 			doTheWorkInNewTransaction( work, transactionManager );
< 		}
< 		else {
< 			doTheWorkInNoTransaction( work,  transactionManager);
< 		}
< 	}
<
< 	private void doTheWorkInNewTransaction(IsolatedWork work, UOWTransactionManagerAdapter transactionManager) {
< 		doTheWork( work , transactionManager, true);
< 	}
<
< 	private void doTheWorkInNoTransaction(IsolatedWork work, UOWTransactionManagerAdapter transactionManager) {
< 		doTheWork( work , transactionManager, false);
< 	}
<
< 	private void doTheWork(IsolatedWork work, UOWTransactionManagerAdapter transactionManager, boolean transacted) {
< 		try {
< 			// obtain our isolated connection
< 			Connection connection = session.getFactory().getConnectionProvider().getConnection();
< 			try {
< 				// do the actual work
< 				if (transacted)
< 					transactionManager.doIsolatedWork(work, connection, false, true);
< 				else
< 					transactionManager.doIsolatedWork(work, connection, false, false);
< 			}
< 			catch ( HibernateException e ) {
< 				throw e;
< 			}
< 			catch ( Exception e ) {
< 				throw new HibernateException( "Unable to perform isolated work", e );
< 			}
< 			finally {
< 				try {
< 					// no matter what, release the connection (handle)
< 					session.getFactory().getConnectionProvider().closeConnection( connection );
< 				}
< 				catch ( Throwable ignore ) {
< 					log.info( "Unable to release isolated connection [" + ignore + "]" );
< 				}
< 			}
< 		}
< 		catch ( SQLException sqle ) {
< 			throw JDBCExceptionHelper.convert(
< 					sqlExceptionConverter(),
< 					sqle,
< 					"unable to obtain isolated JDBC connection"
< 			);
< 		}
< 	}
<
< 	private SQLExceptionConverter sqlExceptionConverter() {
< 		return session.getFactory().getSQLExceptionConverter();
< 	}
<
< }
diff -r -d -N src/org/hibernate/transaction/WebSphereUOWTransactionLookup.java ..\..\hib-orig\src/org/hibernate/transaction/WebSphereUOWTransactionLookup.java
1,304d0
< package org.hibernate.transaction;
<
< import java.lang.reflect.InvocationHandler;
< import java.lang.reflect.InvocationTargetException;
< import java.lang.reflect.Method;
< import java.lang.reflect.Proxy;
< import java.sql.Connection;
< import java.util.Properties;
<
< import javax.naming.NamingException;
< import javax.transaction.HeuristicMixedException;
< import javax.transaction.HeuristicRollbackException;
< import javax.transaction.InvalidTransactionException;
< import javax.transaction.NotSupportedException;
< import javax.transaction.RollbackException;
< import javax.transaction.Status;
< import javax.transaction.Synchronization;
< import javax.transaction.SystemException;
< import javax.transaction.Transaction;
< import javax.transaction.TransactionManager;
< import javax.transaction.xa.XAResource;
<
< import org.hibernate.HibernateException;
< import org.hibernate.engine.transaction.IsolatedWork;
< import org.hibernate.transaction.WebSphereUOWTransactionLookup.UOWTransactionManagerAdapter.UOWTransactionAdapter;
< import org.hibernate.util.NamingHelper;
<
< /**
<  * @author rafaelri Support for proprietary interfaces for registering
<  *         synchronizations in WebSphere 6.1+
<  */
< public class WebSphereUOWTransactionLookup implements TransactionManagerLookup {
<
< 	private static final String UOW_MANAGER_JNDI = "java:comp/websphere/UOWManager";
< 	private static final String UOW_SYNCHRONIZATION_MANAGER_JNDI = "java:comp/websphere/UOWSynchronizationRegistry";
<
< 	public TransactionManager getTransactionManager(Properties props)
< 			throws HibernateException {
< 		return new UOWTransactionManagerAdapter(props);
< 	}
<
< 	public String getUserTransactionName() {
< 		return "java:comp/UserTransaction";
< 	}
<
< 	public static class UOWTransactionManagerAdapter implements TransactionManager {
< 		private final Object uowManager;
< 		private final Class uowManagerClass;
< 		private final Object uowSynchronizationRegistry;
< 		private final Class uowSynchronizationRegistryClass;
< 		private final Class uowActionClass;
< 		private final Method registerSynchronizationMethod;
< 		private final Method setRollbackOnlyMethod;
< 		private final Method setUOWTimeoutMethod;
< 		private final Method getUOWTimeoutMethod;
< 		private final Method runUnderUOWMethod;
< 		public final int UOW_TYPE_GLOBAL_TRANSACTION;
< 		public final int UOW_TYPE_LOCAL_TRANSACTION;
<
< 		private final Properties properties;
< 		private final Method getLocalIdMethod;
<
< 		private UOWTransactionManagerAdapter(Properties props) {
< 			this.properties = props;
< 			try {
< 				uowManager = NamingHelper.getInitialContext(props).lookup(
< 						UOW_MANAGER_JNDI);
< 				uowManagerClass = Class.forName("com.ibm.wsspi.uow.UOWManager");
< 				setRollbackOnlyMethod = uowManagerClass.getMethod(
< 						"setRollbackOnly", new Class[] {});
< 				uowActionClass = Class.forName("com.ibm.wsspi.uow.UOWAction");
< 				runUnderUOWMethod = uowManagerClass.getMethod("runUnderUOW",
< 						new Class[] {int.class, boolean.class, uowActionClass});
< 				setUOWTimeoutMethod = uowManagerClass.getMethod(
< 						"setUOWTimeout", new Class[] { int.class, int.class });
< 				getUOWTimeoutMethod = uowManagerClass.getMethod(
< 						"getUOWTimeout", new Class[] {});
<
< 				uowSynchronizationRegistry = NamingHelper.getInitialContext(
< 						props).lookup(UOW_SYNCHRONIZATION_MANAGER_JNDI);
< 				uowSynchronizationRegistryClass = Class
< 						.forName("com.ibm.websphere.uow.UOWSynchronizationRegistry");
<
< 				UOW_TYPE_LOCAL_TRANSACTION = uowSynchronizationRegistryClass.getDeclaredField("UOW_TYPE_LOCAL_TRANSACTION").getInt(null);
<
< 				UOW_TYPE_GLOBAL_TRANSACTION = uowSynchronizationRegistryClass.getDeclaredField("UOW_TYPE_GLOBAL_TRANSACTION").getInt(null);
<
< 				registerSynchronizationMethod = uowSynchronizationRegistryClass
< 						.getMethod("registerInterposedSynchronization",
< 								new Class[] { Synchronization.class });
< 				getLocalIdMethod = uowSynchronizationRegistryClass.getMethod(
< 						"getLocalUOWId", new Class[] {});
<
< 			} catch (ClassNotFoundException cnfe) {
< 				throw new HibernateException(cnfe);
< 			} catch (NoSuchMethodException nsme) {
< 				throw new HibernateException(nsme);
< 			} catch (NamingException ne) {
< 				throw new HibernateException(ne);
< 			} catch (IllegalArgumentException e) {
< 				throw new HibernateException(e);
< 			} catch (SecurityException e) {
< 				throw new HibernateException(e);
< 			} catch (IllegalAccessException e) {
< 				throw new HibernateException(e);
< 			} catch (NoSuchFieldException e) {
< 				throw new HibernateException(e);
< 			}
< 		}
<
< 		public void doIsolatedWork(IsolatedWork work, Connection conn, boolean join, boolean global) throws SystemException, InvalidTransactionException, IllegalStateException {
< 			Object p = Proxy.newProxyInstance
< 	          (uowActionClass.getClassLoader(),
< 	                new Class[] { uowActionClass }, new UOWInvocationHandler(work, conn));
< 			try {
< 				runUnderUOWMethod.invoke(uowManager, new Object[]{global ? UOW_TYPE_GLOBAL_TRANSACTION : UOW_TYPE_LOCAL_TRANSACTION, Boolean.FALSE.booleanValue(),p});
< 			} catch (IllegalArgumentException e) {
< 				throw new HibernateException(e);
< 			} catch (IllegalAccessException e) {
< 				throw new HibernateException(e);
< 			} catch (InvocationTargetException e) {
< 				throw (HibernateException) e.getTargetException().getCause();
< 			}
< 		}
<
< 		public void begin() throws NotSupportedException, SystemException {
< 			throw new UnsupportedOperationException();
< 		}
<
< 		public void commit() throws RollbackException, HeuristicMixedException,
< 				HeuristicRollbackException, SecurityException,
< 				IllegalStateException, SystemException {
< 			throw new UnsupportedOperationException();
< 		}
<
< 		public int getStatus() throws SystemException {
< 			throw new UnsupportedOperationException();
< 		}
<
< 		public Transaction getTransaction() throws SystemException {
< 			return new UOWTransactionAdapter(properties);
< 		}
<
< 		public void resume(Transaction txn) throws InvalidTransactionException,
< 				IllegalStateException, SystemException {
< 			throw new UnsupportedOperationException();
< 		}
<
< 		public void rollback() throws IllegalStateException, SecurityException,
< 				SystemException {
< 			throw new UnsupportedOperationException();
< 		}
<
< 		public void setRollbackOnly() throws IllegalStateException,
< 				SystemException {
< 			try {
< 				setRollbackOnlyMethod.invoke(uowManager, new Object[] {});
< 			} catch (IllegalArgumentException e) {
< 				throw new RuntimeException(e);
< 			} catch (IllegalAccessException e) {
< 				throw new RuntimeException(e);
< 			} catch (InvocationTargetException e) {
< 				// is there any way to better handle this???
< 				if (e.getTargetException() instanceof IllegalStateException)
< 					throw ((IllegalStateException) e.getTargetException());
< 				else if (e.getTargetException() instanceof SystemException)
< 					throw ((SystemException) e.getTargetException());
< 				else
< 					throw new RuntimeException(e.getTargetException());
< 			}
< 		}
<
< 		public void setTransactionTimeout(int i) throws SystemException {
< 			try {
< 				setUOWTimeoutMethod.invoke(uowManager, new Object[] {
< 						UOW_TYPE_GLOBAL_TRANSACTION,
< 						i });
< 			} catch (IllegalArgumentException e) {
< 				throw new RuntimeException(e);
< 			} catch (IllegalAccessException e) {
< 				throw new RuntimeException(e);
< 			} catch (InvocationTargetException e) {
< 				// is there any way to better handle this???
< 				if (e.getTargetException() instanceof IllegalStateException)
< 					throw ((IllegalStateException) e.getTargetException());
< 				else if (e.getTargetException() instanceof SystemException)
< 					throw ((SystemException) e.getTargetException());
< 				else
< 					throw new RuntimeException(e.getTargetException());
< 			}
< 		}
<
< 		public Transaction suspend() throws SystemException {
< 			throw new UnsupportedOperationException();
< 		}
<
< 		public class UOWTransactionAdapter implements Transaction {
<
< 			private Object localId;
<
< 			private UOWTransactionAdapter(Properties props) {
< 				try {
< 					localId = getLocalIdMethod.invoke(
< 							uowSynchronizationRegistry, new Object[] {});
< 				} catch (Exception e) {
< 					throw new HibernateException(e);
< 				}
< 			}
<
< 			public void registerSynchronization(
< 					final Synchronization synchronization)
< 					throws RollbackException, IllegalStateException,
< 					SystemException {
<
< 				try {
< 					registerSynchronizationMethod.invoke(
< 							uowSynchronizationRegistry,
< 							new Object[] { synchronization });
< 				} catch (Exception e) {
< 					throw new HibernateException(e);
< 				}
<
< 			}
<
< 			public int hashCode() {
< 				return getLocalId().hashCode();
< 			}
<
< 			public boolean equals(Object other) {
< 				if (!(other instanceof UOWTransactionAdapter))
< 					return false;
< 				UOWTransactionAdapter that = (UOWTransactionAdapter) other;
< 				return getLocalId().equals(that.getLocalId());
< 			}
<
< 			private Object getLocalId() {
< 				return localId;
< 			}
<
< 			public void commit() throws RollbackException,
< 					HeuristicMixedException, HeuristicRollbackException,
< 					SecurityException, IllegalStateException, SystemException {
< 				throw new UnsupportedOperationException();
< 			}
<
< 			public boolean delistResource(XAResource resource, int i)
< 					throws IllegalStateException, SystemException {
< 				throw new UnsupportedOperationException();
< 			}
<
< 			public boolean enlistResource(XAResource resource)
< 					throws RollbackException, IllegalStateException,
< 					SystemException {
< 				throw new UnsupportedOperationException();
< 			}
<
< 			public int getStatus() throws SystemException {
< 				return new Integer(0).equals(getLocalId()) ? Status.STATUS_NO_TRANSACTION
< 						: Status.STATUS_ACTIVE;
< 			}
<
< 			public void rollback() throws IllegalStateException,
< 					SystemException {
< 				throw new UnsupportedOperationException();
< 			}
<
< 			public void setRollbackOnly() throws IllegalStateException,
< 					SystemException {
< 				try {
< 					setRollbackOnlyMethod.invoke(uowManager, new Object[] {});
< 				} catch (Exception e) {
< 					throw new HibernateException(e);
< 				}
< 			}
< 		}
<
< 	}
<
< 	public Object getTransactionIdentifier(Transaction txn) {
< 		if (!(txn instanceof UOWTransactionAdapter))
< 			throw new IllegalStateException("Invalid Transaction class");
< 		UOWTransactionAdapter tx = (UOWTransactionAdapter) txn;
< 		return tx.localId;
< 	}
<
< 	static class UOWInvocationHandler implements InvocationHandler {
<
< 		private IsolatedWork work;
< 		private Connection connection;
<
< 		public UOWInvocationHandler(IsolatedWork work, Connection connection) {
< 			super();
< 			this.work = work;
< 			this.connection = connection;
< 		}
< 		public Object invoke(Object proxy, Method method, Object[] args)
< 				throws Throwable {
< 			work.doWork(connection);
< 			return void.class;
< 		}
<
< 	}
<
< }

24
May
10

Instrumentation and Monitoring as means of problem detection and resolution

While designing the last big project I worked on we chose to place some monitoring code in specific points of the application. Those metrics were meant for emitting information targeted at business users but also to technical users, this information was then split according to audience through the respective JMX agents used (was it a Tivoli ITCAM agent or a JOPR agent).
At first we were expecting only that this could provide us valuable information for live environment such as when we had anything abnormal on the legacy software we were integrating with but we ended up noticing that this could also provide us valuable informal of the operation behaviour of our software and the best part: on live environment. And in fact it turns out that this is so common that we can find others pointing into this direction as well.
The picture below presents an overview of the application in question as well as the instrumentation points.

Application overview with instrumentation points

Application overview with instrumentation points

The instrumentation points gathered the following information:

  • The instrumentation point on the left of the OuterMDB collected the sum of the messages processed in the current and last hour as well as the messages per second.
  • The instrumentation point on depicted in the top of the OuterMDB collected the sum of the time spent in pre-processing as well as the number of times pre-processing was invoked.
  • The instrumentation point on top of the InnerMDB collected the sum of messages processed in the current and last hour as well as the messages per second.
  • And finally, the instrumentation point on the bottom of the InnerMDB collected the sum of time spent in the communication with the legacy system as well as the average of processing time per request in the current and last hour, the minimum and maximum times of processing for current and last hour and the amount of request processed as well as the timeouts.

The comparison between the number of messages processed in the InnerMDB and OuterMDB could provide us means of comparing how we should size the Thread pools for each of these components. This is such an information that would be harder to obtain by any other means. We also used those metrics for detecting misfunction on the pre-processing legacy software that was invoked by our PreProcessing component, this way we could switch off pre-processing and avoid a negative impact on overall system performance.
But this monitoring was key to the detection of a misbehavior of our JCA connector. A misimplementation of the equals/hashcode method pair for the ManagedConnection lead to a huge performance degradation after a few hours of software operation. By using our monitoring infrastructure we could isolate the problematic code area. Sure it did not point towards the equals/hashcode pair but it was clear that it was related to connection acquisition code.
Finally, the monitoring in our application provided us an effective way of monitoring the legacy application we were communicating with since it did not provide any native way of monitoring its operation. We were then able to instantly respond to outages on the legacy application through metrics collected on our software.

20
May
10

Designing for Extreme Transaction Processing – Memento Pattern

Applications with huge transaction processing requirements or tight response times always result in a careful architecture design. Special care has to be taken on how to partition data in order to be able to better parallelize load. This task can be even trickier if some of the data isn’t suitable for proper partitioning. Being ableto partition data is an essential requirement for some of the elastic frameworks around – some of them even demand that data is local to the node processing the request while others may still work but with a significant performance drop if it is not. This negative impact due to difficulties in the proper partitioning and grouping of data can be mitigated at the cost of increased memory usage as it is always possible to increase replication forcing data to be in multiple nodes to avoid serialization upon request as there isn’t the concept of gravitation on such tools as there was on previous distributed caches.
Billy Newport (the mind behind IBM eXtreme Scale) proposed a classification scheme for the different styles of Extreme Transaction Processing (XTP) systems he identified:

  • Type 1: Non partitionable data models – Applications in this category usually perform adhoc queries that can span an unpartitionable amount of data which leaves scaling up as the only scaling possibility.
  • Type 2a: Partitionable data models with limited needs for scaling out – Applications in this category already present means of partitioning data and thus load but still on a limited fashion. Hence those applications can still be built using regular application servers backed by Sharded Databases, elastic NoSQL databases or IMDGs but still won’t use a Map/Reduce pattern for processing requests.
  • Type 2b: Partitionable data models with huge needs for scaling out – Finally this category is composed of applications that presents means of partitioning data as on Type 2a but instead of being exposed to limited load, Type 2b are applications are pushed to the limit. Type 2b applications usually evolved from Type 2a and were faced with the scalability limits of traditional architectures moving from regular application servers to Map/Reduce frameworks. It is worth noting that it is possible to scale to similar load requirements with traditional architectures based on regular application servers but they’ll usually require more human resources for administration as well as more hardware infrastructure.

Among the list of common items on a classic Transaction Processing (TP) system that must be avoided on an XTP system are two phase commit resources. Note that it is not that you can’t have a two phase commit resource as part of an XTP but they must be carefully evaluated and excess will certainly compromise system performance.
Billy presented (at the eXtreme Scale meet the experts session@IBM Impact 2010) an excellent example scenario on where an excess of two phase commit resources could undermine the performance of an e-commerce solution. In his example, the checkout process of the referred e-commerce site would, as part of a single transaction, scan the whole shopping cart and for each product in the cart it would then perform an update on a fictional inventory database as well as updating a few other two phase commit databases from other departments. If any of the items were out of stock transaction would be rolled back and an error would be presented to the user. It is obvious that this hypothetical system wouldn’t be able to scale much since the cost related to the long time span of this transaction combined with the number of resources involved would be tremendous. Not to mention that there would be a huge load on the transaction manager.
Instead of using this rather obvious approach, Billy suggested that these updates could be chained and combined with the Memento design pattern – the updates would then be sequentially applied and if any of them failed the Memento pattern would then be used to revert the changes that have already been applied. Using this approach the contention on all the databases involved would be minimal and the application requirement would still be fulfilled.
This is one of the many examples we can point that need to be carefully observed when designing XTP systems.

04
Apr
10

Embedding Cassandra into your JavaEE AppServer

Recently I started some tests with Apache Cassandra. Sincerely I got impressed with its aptitude to horizontally scale and therefore to handle load increase. Among its main virtues are:

  • Ability of replacing failed nodes without downtime
  • Complete absence of a single point for failure. Every node is identical in regard to functionality

not to mention a few other features.
But still there is one point that bothers me: I still see it and its API as a foundation thing. Something like JGroups turned into. JGroups is largely used nowadays but rarely directly by the application developer. You use it indirectly when you use a clustered JBoss or JBoss TreeCache or Infinispan.
One of these responsibilities that still lies on application developer is the fail over capacity. When you connect to a Cassandra Database node through your application you still need to fetch through its JMX API the rest of the nodes that are part of this cluster, otherwise if this node fails even though your Cassandra cluster is still up and healthy your application won’t know how to connect to it. Another possibility (and in fact this is a recommendation even if you retrieve this node list) is to have a list of reliable servers to serve as bootstrap connection servers but remember Murphy Law even all the servers on this list may go down so you still need to retrieve the whole list for failing over.
So, in summary we have something like what is depicted on the following picture:

Cassandra Regular usage thru JCA and Thrift

Cassandra Regular usage thru JCA and Thrift


Note that there is an iherent complexity in this solution, the JCA connector will be responsible for keeping a list of fail over nodes, something a Cassandra instance already does, so we end up violating the DRY principle.
But what alternatives do we have?

The StorageProxy API


It turns out that CassandraDaemon class (the main one responsible for starting up a Cassandra node) does only a few things that we can embed into our application, or should I say into our JCA Connector since in a JavaEE this is the only place you should be spawning threads and opening files directly from filesystem.
In fact those few steps are properly described on Cassandra Wiki.
Then if you take what we could call a regular approach you’d spawn the embedded Cassandra and perform a connection to localhost in order to communicate make the application talk to the Cassandra Server you’ll end up having an unecessary network communication that could reduce performance by increasing latency. It turns out that this can be avoided too by using the (not so stable) StorageProxy API.
By taking all the steps describe above you’d end up with a much simpler architecture as the one below:

Cassandra Embedded into JavaEE Server

Cassandra Embedded into JavaEE Server


With this architecture you are shielded from the complexity of failing over, Cassandra handles this automatically for you. Then you could argue: what if I need to independently scale the Cassandra layer? No problem! You can resort to an hybrid architecture like the one below:
Cassandra Embedded with Extra Nodes

Cassandra Embedded with Extra Nodes


In order to achieve this you only need to provide to these extra nodes the address of some of the JavaEE servers, this way, the extra standalone nodes can communicate with Cassandra daemons inside the AppServer and become part of the Cassandra cluster.

Memory mapped TTransport


My first attempt on doing this with Cassandra involved implementing a TTransport class that would be responsible for sending over a byte buffer commands to the server worker thread and receiving response in a similar fashion. I tried this first due to the complete ignorance of the existence of the StorageProxy API. But later I thought this could solve the issue related to the lack of stability this API has (as per the apache Wiki page). But this turned to be a not so easy task.
I thought of having a CassandraMMap that would act as the CassandraDaemon class but it would differ on TThreadPoolServer initialization as below:

		this.serverTransp = new MMapServerTransport();
		TThreadPoolServer.Options options = new TThreadPoolServer.Options();
		options.minWorkerThreads = 64;
		this.serverEngine = new TThreadPoolServer(new TProcessorFactory(
				processor), serverTransp, inTransportFactory,
				outTransportFactory, tProtocolFactory, tProtocolFactory,
				options);

The same instance of MMapServerTransport would be handled to the client through a getter method in order to open client connections as follows:
		TTransport tr = mmap.getServerTransp().getClientTransportFactory()
				.getTransport(null);
		TProtocol proto = new TBinaryProtocol(tr);
		Cassandra.Client client = new Cassandra.Client(proto);
		tr.open();

Requests through getTransport would be queued on server using the class below and a TTransport for the server would be returned upon acceptImpl:
public class MMapTransportTuple {
	private TByteArrayOutputStream server2Client;
	private TByteArrayOutputStream client2Server;
	private TTransport clientTransport;
	private TTransport serverTransport;

	public MMapTransportTuple(int size) {
		server2Client = new TByteArrayOutputStream(size);
		client2Server = new TByteArrayOutputStream(size);
		clientTransport = new MMapTransport(server2Client, client2Server);
		serverTransport = new MMapTransport(client2Server, server2Client);
	}
        //certain codes ommited for brevity

This class would be responsible for binding the memory buffers from client to server and vice-versa.
The last class involved in this implementation would be the MMapTransport:
public class MMapTransport extends TTransport {

	private TByteArrayOutputStream readFrom;

	private TByteArrayOutputStream writeTo;

	/**
	   *
	   */
	public MMapTransport(TByteArrayOutputStream readFrom,
			TByteArrayOutputStream writeTo) {
		this.readFrom = readFrom;
		this.writeTo = writeTo;
	}
        //read and write would operate on the respective buffer
        //and they would point to different buffers on client and server TTransport instances...
}

But this turned to be harder than I thought at first and as time became a short resource I’ll stick with the StorageProxy API approach for now.

21
Mar
10

Replacing Persistence Caching with In Memory DataGrids

Anyone that designed an enterprise system in Java with non-functional requirements that demanded tight response times faced the challenge of tuning the overall solution. If you are using Hibernate for persistence your first idea is to turn on Hibernate Caching but unfortunately it only Caches loads through Entity Id and sometimes you need to lookup through a secondary unique key. Another solution you might think is turning on Query Cache but as this post points out is more harmful to scalability and latency than you can ever imagine. Although Hibernate Docs and the post referenced above suggest that using @NaturalId annotation and Querying using Criteria API and a specially crafted Restriction through naturalId method would alleviate much of that problem it turns out that updates on related tables hugely undermines the improvements NaturalId queries may bring as this post suggest:

[...] Even though the objects are essentially static, the query cache takes the safe route and says any change to a related table invalidates any and all queries that reference that table. This makes sense, from a generic cache standpoint. [...]

Source: Hibernate: Cache Queries the Natural Id Way
Add to this the fact that second level hibernate caches are data caches, not object caches so even on the best case of a cache miss you’d still have the cost of hydrating the object (as it is called on Hibernate terminology).

Object Caching

So, after giving up on all those previous attempts you’ll start considering taking the next obvious path: using/implementing an Object Caching. Have you noticed that I mentioned implementing? Yes, implementing. Now you’ll probably have to take care of the loading process in the case of cache misses, in fact you’ll be using a pattern called Side Caching.
But this can become a tedious code for maintaining and very prone to errors not to mention that implementing asynchronous write-behind won’t be an easy task.

In Memory DataGrids

Fortunately newer solutions marketed as In Memory DataGrids (or IMDG for short) provide an almost automagic way of plugging specially crafted classes that would be responsible for querying the underlying persistent storage on Cache misses, not to mention that they also provide improved partitioning and a great amount of other facilities usually not available on regular caches.
WebSphere eXtreme Scale Loaders and JBoss InfiniSpan CacheStore are examples of such specially crafted extensions for IMDGs. Those APIs also allow for updating the underlying storage (even asynchronously) with the changes that were passed to the in memory cache. One thing still missing (on different extents) on both solutions is the ability of querying the underlying store using a natural key. On eXtreme Scale there is already a framework for loading JPA entities but it lacks the ability of querying the underlying storage by anything different than the Cache Key that in those cases defaults to Entity Id (eXtreme Scale Query API is capable of querying the cache through any eXtreme Scale entity attribute but it only sees the data in memory). InfiniSpan on the other hand as of the 4.0.0 release does not provide anything similar to eXtreme Scale JPALoader or JPAEntityLoader it still sees the database (as on its JDBC Based CacheStore) as means of providing offloading and surviving reloads but never as means of querying data based on application defined format (eg.: application entities). Instead it uses its own format for storing data which limits its capability of loading from application storage in case of cache misses.

Querying IMDGs through Natural Keys

In summary, if you need to query through a natural key, in both cases you’ll need to roll your own Loader/CacheStore. And by querying through a natural key I mean something along the lines of what is described on Bill Burke’s EJB3 book:

The first way to define a primary-key class (and, for that matter, composite keys) is to use the @IdClass annotation. Your bean class
does not use this primary-key class internally, but it does use it to interact with the entity manager when finding a persisted object through
its primary key.
[...]
In your bean class, you designate one or more properties that make up your primary key, using the @Id annotation. These properties
must map exactly to properties in the @IdClass.

This way you can query the Cache using something in the lines of:

MyObjectNaturalKey key = new MyObjectNaturalKey("keyField1", "keyField2");
MyObject o = (MyObject) cache.get(key);

And you’ll have the guarantee that if this object exists either already on memory or in JPA EntityManager it’ll be returned.

Implementing a InfiniSpan CacheStore

Infinispan’s CacheStore interface has the following methods:

package org.infinispan.loaders;
public interface CacheStore extends CacheLoader {
   void store(InternalCacheEntry entry) throws CacheLoaderException;
   void fromStream(ObjectInput inputStream) throws CacheLoaderException;
   void toStream(ObjectOutput outputStream) throws CacheLoaderException;
   void clear() throws CacheLoaderException;
   boolean remove(Object key) throws CacheLoaderException;
   void removeAll(Set<Object> keys) throws CacheLoaderException;
   void purgeExpired() throws CacheLoaderException;
   void prepare(List<? extends Modification> modifications, GlobalTransaction tx, boolean isOnePhase) throws CacheLoaderException;
   void commit(GlobalTransaction tx) throws CacheLoaderException;
   void rollback(GlobalTransaction tx);
   public CacheStoreConfig getCacheStoreConfig();
}

If you need further explanation of what each method does, see its JavaDoc. InfiniSpan team suggests having a look on DummyInMemoryCacheStore in order to have a general idea of how should a CacheStore be implemented but still I have the feeling it misses an explanation of how InternalCacheEntries should be instantiated and in fact there is a factory class for it.
At last, if you need further information JBoss InfiniSpan Wiki has an overview of the builtin CacheStore classes.

Implementing an eXtreme Scale Loader

eXtreme Scale has plenty information on how to write a Loader as well. InfoCenter has two important pages: the first one presents a general overview on how to write a loader, the second one presents one important concept of eXtreme Scale: its tuples. If you implement a Loader that stores its data based on tuples you are free from having to have the stored classes (in case of custom application classes) in the same classloader as the container process.
WebSphere eXtreme Scale Wiki has a page dedicated to explaining how to write a Loader which presents the signature of a class that implements the Loader interface:

package com.ibm.websphere.objectgrid.plugins;
public interface Loader {
    static final SpecialValue KEY_NOT_FOUND;
    List get(TxID txid, List keyList, boolean forUpdate) throws LoaderException;
    void batchUpdate(TxID txid, LogSequence sequence) throws LoaderException, OptimisticCollisionException;
    void preloadMap(Session session, BackingMap backingMap) throws LoaderException;
}

Apart from the Wiki there’s a post on Billy New Port’s DevWebSphere blog
that has the source code available for what he calls the PureQuery Loader for eXtreme Scale. It is a loader capable of loading POJOs from SQL queries.
Finally if you need a starting guide for eXtreme Scale, have a look at ObjectGrid programming model guide and samples that although slighlty old is still a good starting guide for WebSphere eXtreme Scale.
Lastly I’d like to say that Packt Publishing book on eXtreme Scale is also a good reference for it.

05
Jan
10

Quartz Resource Adapter as an alternative to EJB Timers

Anyone developing a J2EE application probably faced the challenge of setting some fixed interval timers that need to be up upon application startup. This, in fact involves two challenges:

First one: J2EE provides no means of startup notification on EJB-Jars.

Second: EJB timers are persistent (they survive server restarts) and if you don’t cancel them upon application shutdown you’ll end up having lots of those timers.

So, wouldn’t it be fine if we had some mechanism of having a notification sent down from AppServer to our application in a timed manner? It’d be even better if we had some mechanism to specify the intervals using Unix Cron format.

Happily there is a simple solution: Quartz Resource Adapter from JBoss licensed as LGPL (as stated on its ra.xml) that makes it safely usable on commercial products. It comes packaged as a rar file on JBoss deploy directory as it seems to be used for JBoss internal purposes but nothing (or almost nothing) prevents you from using it into another AppServer.

By almost nothing I meant that as it is packaged on JBoss there is one quirk:

QuartzResourceAdapter.endpointActivation method does one trick to detect whether the endpoint configured is a stateless or stateful endpoint that renders it unusable on WebSphere for example (WebSphere prevents you from calling MessageEndpointFactory.createEndpoint from the Thread that invoked endpointActivation) and I am sincerely not sure whether this is something the spec enforces. I’ll paste the trick below:

 // allocate instance of endpoint to figure out its endpoint interface
      Class clazz = QuartzJob.class;
      MessageEndpoint tmpMe = endpointFactory.createEndpoint(null);
      if (tmpMe instanceof StatefulJob) clazz = StatefulQuartzJob.class;
      tmpMe.release();

And the best thing is that this trick can be safely replaced by two markup ActivationSpec classes, so we end up having the following code:

      // figure out endpoint interface through activationspec (WAS denies endpoint creation on main thread)
      Class clazz = null;
      if (spec instanceof QuartzStatelessActivationSpec) {
          clazz = QuartzJob.class;
      }
      else {
          clazz = StatefulQuartzJob.class;
      }

And the two markup classes:

package org.jboss.resource.adapter.quartz.inflow;

public class QuartzStatefulActivationSpec extends QuartzActivationSpec {

}

package org.jboss.resource.adapter.quartz.inflow;

public class QuartzStatelessActivationSpec extends QuartzActivationSpec {

}

And the ra.xml file for the RAR package gets changed from:

 <messagelistener>
               <messagelistener-type>org.quartz.Job</messagelistener-type>
               <activationspec>
                  <activationspec-class>org.jboss.resource.adapter.quartz.inflow.QuartzActivationSpec</activationspec-class>
                  <required-config-property>
                      <config-property-name>cronTrigger</config-property-name>
                  </required-config-property>
               </activationspec>
            </messagelistener>
            <messagelistener>
               <messagelistener-type>org.quartz.StatefulJob</messagelistener-type>
               <activationspec>
                  <activationspec-class>org.jboss.resource.adapter.quartz.inflow.QuartzActivationSpec</activationspec-class>
                  <required-config-property>
                      <config-property-name>cronTrigger</config-property-name>
                  </required-config-property>
               </activationspec>
            </messagelistener>

To:

 <messagelistener>
               <messagelistener-type>org.quartz.Job</messagelistener-type>
               <activationspec>
                  <activationspec-class>org.jboss.resource.adapter.quartz.inflow.QuartzStatelessActivationSpec</activationspec-class>
                  <required-config-property>
                      <config-property-name>cronTrigger</config-property-name>
                  </required-config-property>
               </activationspec>
            </messagelistener>
            <messagelistener>
               <messagelistener-type>org.quartz.StatefulJob</messagelistener-type>
               <activationspec>
                  <activationspec-class>org.jboss.resource.adapter.quartz.inflow.QuartzStatefulActivationSpec</activationspec-class>
                  <required-config-property>
                      <config-property-name>cronTrigger</config-property-name>
                  </required-config-property>
               </activationspec>
            </messagelistener>

Apart from this you’ll have to include jboss-common.jar (found on $JBOSS_HOME\lib) into the quartz-ra.rar file in the same level as quartz-ra.jar and quartz.jar.
Now you are done to install it into your application server and finally create your Activation Specs.
I am providing a PDF with Patched Quartz Resource Adapter for anyone willing to test on your preferred J2EE server and I also posted a message on JBoss AS Development forum to check whether they are interested in incorporating these changes.
Updates: It seems like this is going to get integrated into JBoss AS main code. I’ve submitted the patch as requested.




Follow

Get every new post delivered to your Inbox.