Posts Tagged ‘WebSphere

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;
< 		}
<
< 	}
<
< }
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.

19
Dec
09

WebSphere eXtreme Scale 6 book

As already stated I’ve been invited to review WebSphere eXtreme Scale book from Packt Publishing. The book is an excellent choice for anyone interested in adopting WebSphere eXtreme Scale in a solution.

WebSphere eXtreme Scale

WebSphere eXtreme Scale Book

The book starts on chapter one with basic concepts about a data grid and ends up with a hello world like application.

On chapter two it starts to take a deeper dive into WebSphere eXtreme Scale concepts. The first one explored is the ObjectMap API which is composed by the ObjectMap and the BackingMap objects.

Chapter three is the one that specially took my attention since it presented me a feature I had no clue eXtreme Scale had: a JPA like implementation. This is one of the things that makes eXtreme Scale such a nice tool. By now you will probably be thinking: “but what is the difference of having this and eXtreme Scale acting as an ORM cache?”. In fact there’s a great deal of differences – first one: on an ORM solution you’re caching data that when “hydrated” (as Hibernate calls it – this article explains something about hibernate 2nd level cache) becomes objects, so, you still have the time lapse and the memory losses derived from hydrating objects on each request. Another major advantage is that ORM caches are usually invalidated upon some update scenarios which leads to reload of data.

Chapter four covers how to integrate a data grid with a database store. As the book states you’d need to integrate with a database backend to provide a richer and wider set of tools for report generation, to integrate with legacy application that still interact with database, etc.

Chapter five is dedicated to handling increased load. If you thought: “Do I still need to care about load increase?” the answer is “YES! For Sure!”. All and every software needs to be proper configured for increased load, by no means eXtreme Scale would be an exception and this chapter covers important topics of performing planning  for increased load.

Chapter six is devoted to keeping data available, so now it focus on replication and other important points.

Chapter seven (which is also freely available online) presents a rather different pattern of performing tasks in a distributed application – pushing the operation instead of pulling the data.

Chapter eight is dedicated to present some common patterns when using a data grid.

Chapter nine covers some facilities for Spring integration.

And finally chapter ten covers a complete example of a project that is built open eXtreme Scale.

One thing I’d like to see on the books next edition is the ability of replacing WebSphere’s Application Server DynaCache with an eXtreme Scale cache implementation. But it is no surprise for me that this was missing on book since they (the book and this feature) were released almost at the same time, invalidating any possibility of this being covered on the book.

In summary: this is a book I really recommend for anyone interested in picking a data grid solution for Java. It is also more recommended if you are already inclined to use an IBM solution and stick with eXtreme Scale.

16
Nov
09

WebSphere eXtreme Scale 6 book review

Anyone designing a high performance transaction processing system has already needed at least a caching solution for increasing response time on frequently used data. But sometimes these data might become bigger than usual, those are times when you need a sofisticate

WebSphere eXtreme Scale

WebSphere eXtreme Scale Book

solution, something that can automatically fetch data from a slower storage, offload when not necessary, split its contents between various nodes, …

If you require something like this then you’ll notice that you need a DataGrid solution.

By now you’ll be asking yourself why I am on that subject? Recently I’ve been kindly invited to review a book about IBM WebSphere eXtreme Scale from Packt Publishing.

Apart from this, I work on a project where we were already evaluating WebSphere eXtreme Scale as a replacement for WebSphere DynaCache.

If you are in a hurry and want to have a sneak peak on the book contents, take a look at the sample chapter (Chapter 7: “The DataGrid API”) available for free on Packt Publishing website.

03
May
09

WebSphere Concepts: Cell, Node, Cluster, Server…

Quick post… If you are not familiar with WebSphere at first you might get confused with its concepts: cell, deployment manager, node, node agent, cluster, server, …

First of all, lets start with the concept of a Cell:

A Cell is a virtual unit that is built of a Deployment Manager and one or more nodes. I guess a picture will help making things clearer:

WebSphere Cell

WebSphere Cell

But still there are a few concepts that need to be explained. The next obvious one is the Deployment Manager.

The Deployment Manager is a process (in fact it is an special WebSphere instance) responsible for managing the installation and maintenance of Applications, Connection Pools and other resources related to a J2EE environment. It is also responsible for centralizing user repositories for application and also for WebSphere authentication and authorization.

The Deployment Manager communicates with the Nodes through another special WebSphere process, the Node Agent.

The Node is another virtual unit that is built of a Node Agent and one or more Server instances.

The Node Agent it the process responsible for spawning and killing server processes and also responsible for configuration synchronization between the Deployment Manager and the Node. Extra care must be taken when changing security configurations for the cell, since communication between Deployment Manager and Node Agent is ciphered and secured when security is enabled, Node Agent needs to have configuration fully resynchronized when impacting changes are made to Cell security configuration.

Servers are regular Java process responsible for serving J2EE requests (eg.: serving JSP/JSF pages, serving EJB calls, consuming JMS queues, etc).

And to finish, Clusters are also virtual units that groups Servers so resources added to the Cluster are propagated to every Server that makes up the cluster, this will in fact affect usually more than a single Node instance.

Lets finish this post with another diagram to illustrate all those concepts.

WebSphere Concepts

WebSphere Concepts

28
Mar
09

WebSphere custom user repository

If you already used JBoss you probably used (or at least knew there is) the DatabaseServerLoginModule. But, if you happen to be using WebSphere things are a little bit different. WebSphere (I guess version 6 onwards) comes with the powerful concept of the federated repository. This feature allows multiples sources for authentication and authorization. The final layout inside WebSphere ressembles an LDAP tree, every Repository is suffixed with an identifier.

But, unfortunately, through the Web Administration interface you are limited to LDAP and the builtin FileBased WIM authentication.

Another thing that makes it even harder is that there isn’t much documentation about these extended repositories.

There is one post from IBM that suggests that there are alternatives:

The information used for the registry lookup is customizable and can be made totally flexible if a custom registry is developed using the WebSphere Application Server custom registry interface.

But still there isn’t a single link from this article that points out how to achieve this. If you get the key words in this phrase and risk a Google search you’d end up finding how to do this on WebSphere 4, that, by the way, was much limitted when compared to what is available on WebSphere 6.

Refining this search a little bit you’ll find what you are looking for, how to do this on WebSphere 6. But you’ll only take this approach if you are willing to roll your own adapter, but be advised that this tutorial won’t cover every steps you need and this is the content of another post. There is a complex interface that needs to be implemented in order to fulfill the requirements of an WebSphere Repository. The complexity is derived from the fact that all method in this interface is based on commonj.sdo.DataObject class and this class maps an XML to a Java Object hierarchy making for now this implementation almost a guessing game since there isn't much public information regarding this interface contract.

But, returning to the original objective of the post that was to guide on how to make WebSphere authenticate users against a database, setup the DBAdapter using the guide that is found on this page. If you want, check the list of WebSphere builtin adapters here.

The last step is to create the tables for the database as described on step 3 of this post. This post in fact describes various possibilities for user repositories available in WebSphere.

JBoss DatabaseServerLoginModule adantages over WebSphere approach are its simplicity and flexibility but on the other hand, WebSphere Federated Repositories present a much more robust approach for handling user databases.

I’ll try to post some tips on how to roll your own Repository in the next few days.

01
Feb
09

Monitoring a secured WebSphere installation through JOPR/RHQ

Monitoring an WebSphere installation with security enabled using JOPR/RHQ may seem at first simple and indeed it is if it weren’t for a few missing parts on the JOPR agent and MC4J library.
For those in a hurry that don’t want to wait till it gets incorporated into JOPR and MC4J I’ll try to summarize the changes and also provide the JMX Agent jar to be replaced. Apart from this, there is a need to import the server certificate for WAS SOAP Admin port into the JVM that JOPR is running (I hope there’ll be a certificate management feature in JOPR in the future to make this even simpler).
Let’s get to the changes.
First of all download RHQ code from SVN.

svn co http://svn.rhq-project.org/repos/rhq/tags/RHQ_1_1_0_GA/

Use tag 1.1.0 since I made the changes on top of that version.
Replace the following file on JMX plugin:
JMXServerComponent

/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.plugins.jmx;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mc4j.ems.connection.ConnectionFactory;
import org.mc4j.ems.connection.EmsConnection;
import org.mc4j.ems.connection.local.LocalVMFinder;
import org.mc4j.ems.connection.local.LocalVirtualMachine;
import org.mc4j.ems.connection.settings.ConnectionSettings;
import org.mc4j.ems.connection.support.ConnectionProvider;
import org.mc4j.ems.connection.support.metadata.ConnectionTypeDescriptor;
import org.mc4j.ems.connection.support.metadata.J2SE5ConnectionTypeDescriptor;
import org.mc4j.ems.connection.support.metadata.LocalVMTypeDescriptor;

import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pluginapi.inventory.ResourceContext;

/**
* The generic JMX server component used to create and cache a connection to a local or
* remote JMX MBeanServer. This component is responsible for building an isolated connection/classloader
* to the managed resource's JMX MBeanServer. Each connection is isolated from other connections
* created by other instances of this component. This allows for it to do things like manage
* multiple JBossAS servers that are running on the same box, even if they are of different JBossAS
* versions. The same holds true for Hibernate applications - multiple connections can be created
* to different versions of the Hibernate MBean and due to the isolation of each connection, there
* are no version incompatibility errors that will occur.
*
* @author Greg Hinkle
* @author John Mazzitelli
*/
public class JMXServerComponent implements JMXComponent {
private static Log log = LogFactory.getLog(JMXServerComponent.class);

private EmsConnection connection;
private ConnectionProvider connectionProvider;

ResourceContext context;

public void start(ResourceContext context) throws Exception {
this.context = context;
log.info("Starting connection to JMX Server " + context.getResourceKey());

try {
internalStart();
} catch (Exception e) {
log.warn("JMX Plugin connection failure", e);
// The new model is to always succeed in starting, but warn about the errors (we only do this the first request)
/*throw new Exception("Unable to connect to Java VM ["
+ configuration.getSimple(JMXDiscoveryComponent.CONNECTOR_ADDRESS_CONFIG_PROPERTY).getStringValue()
+ "]", e);*/
}

if (connection == null) {
log.warn("Unable to connect to JMX Server " + context.getResourceKey());
}
}

protected void internalStart() throws Exception {
Configuration configuration = context.getPluginConfiguration();

String connectionTypeDescriptorClass = configuration.getSimple(JMXDiscoveryComponent.CONNECTION_TYPE)
.getStringValue();

if (LocalVMTypeDescriptor.class.getName().equals(connectionTypeDescriptorClass)) {
String commandLine = configuration.getSimple(JMXDiscoveryComponent.COMMAND_LINE_CONFIG_PROPERTY)
.getStringValue();

Map vms = LocalVMFinder.getManageableVirtualMachines();
if (vms != null) {
for (LocalVirtualMachine vm : vms.values()) {
if (vm.getCommandLine().equals(commandLine)) {
connectLocal(vm.getVmid());
}
}
}
} else if (JMXDiscoveryComponent.PARENT_TYPE.equals(connectionTypeDescriptorClass)) {
// We're embedded in another jmx server component without jmxremoting set so just use the parent's connection
this.connection = ((JMXComponent) context.getParentResourceComponent()).getEmsConnection();
this.connectionProvider = this.connection.getConnectionProvider();
} else if (J2SE5ConnectionTypeDescriptor.class.getName().equals(connectionTypeDescriptorClass)) {
// We're embedded in a J2SE VM with jmxremote defined (e.g. for jconsole usage)
String principal = null;
String credentials = null;
PropertySimple o = configuration.getSimple(JMXComponent.PRINCIPAL_CONFIG_PROP);
if (o != null) {
principal = o.getStringValue();
}
o = configuration.getSimple(JMXComponent.CREDENTIALS_CONFIG_PROP);
if (o != null) {
credentials = o.getStringValue();
}

ConnectionSettings settings = new ConnectionSettings();
J2SE5ConnectionTypeDescriptor desc = new J2SE5ConnectionTypeDescriptor();
settings.setConnectionType(desc);
settings.setServerUrl(configuration.getSimple(JMXDiscoveryComponent.CONNECTOR_ADDRESS_CONFIG_PROPERTY)
.getStringValue());
if (principal != null) {
settings.setPrincipal(principal);
}
if (credentials != null) {
settings.setCredentials(credentials);
}

prepareConnection(settings);

} else {
// This can handle internal connections (within the same vm as the plugin container) as well as
// any remote connections
ConnectionSettings settings = new ConnectionSettings();

String principal = null;
String credentials = null;
PropertySimple o = configuration.getSimple(JMXComponent.PRINCIPAL_CONFIG_PROP);
if (o != null) {
principal = o.getStringValue();
}
o = configuration.getSimple(JMXComponent.CREDENTIALS_CONFIG_PROP);
if (o != null) {
credentials = o.getStringValue();
}

settings.initializeConnectionType((ConnectionTypeDescriptor) Class.forName(connectionTypeDescriptorClass)
.newInstance());

settings.setConnectionType((ConnectionTypeDescriptor) Class.forName(connectionTypeDescriptorClass)
.newInstance());
settings.setServerUrl(configuration.getSimple(JMXDiscoveryComponent.CONNECTOR_ADDRESS_CONFIG_PROPERTY)
.getStringValue());

String installPath = configuration.getSimpleValue(JMXDiscoveryComponent.INSTALL_URI, null);
if (installPath != null) {
settings.setLibraryURI(configuration.getSimple(JMXDiscoveryComponent.INSTALL_URI).getStringValue());
}

if (principal != null) {
settings.setPrincipal(principal);
}
if (credentials != null) {
settings.setCredentials(credentials);
}
prepareConnection(settings);
}

this.connection.loadSynchronous(false);

}

public void stop() {
if (connection != null) {
connection.close();
connection = null;
}
}

protected void connectLocal(int vmid) {
// TODO GH: Refactor ems to also accept the vm itself
ConnectionSettings settings = new ConnectionSettings();
settings.setConnectionType(new LocalVMTypeDescriptor());
settings.setServerUrl(String.valueOf(vmid));
prepareConnection(settings);
}

protected void prepareConnection(ConnectionSettings settings) {
settings.getControlProperties().setProperty(ConnectionFactory.COPY_JARS_TO_TEMP, String.valueOf(Boolean.TRUE));
settings.getControlProperties().setProperty(ConnectionFactory.JAR_TEMP_DIR,
this.context.getTemporaryDirectory().getAbsolutePath());

addAdditionalJarsToConnectionSettings(settings);

ConnectionFactory cf = new ConnectionFactory();
cf.discoverServerClasses(settings);

this.connectionProvider = cf.getConnectionProvider(settings);
this.connection = this.connectionProvider.connect();
}

public EmsConnection getEmsConnection() {
return this.connection;
}

public AvailabilityType getAvailability() {
if (connectionProvider == null || !connectionProvider.isConnected()) {
try {
internalStart();
} catch (Exception e) {
log.debug("Still unable to reconnect resource: " + context.getResourceKey() + " due to error: "
+ e.getMessage());
}
}

return ((connectionProvider != null) && connectionProvider.isConnected()) ? AvailabilityType.UP
: AvailabilityType.DOWN;
}

public List discoverServices(ResourceType type, Configuration defaultPluginConfiguration) {
defaultPluginConfiguration.getSimple("objectName").getStringValue();

return null;
}

private void addAdditionalJarsToConnectionSettings(ConnectionSettings settings) {
// get the plugin config setting that contains comma-separated list of files/dirs to additional jars
// if no additional classpath entries are specified, we'll return immediately and not do anything to settings
Configuration pc = context.getPluginConfiguration();
PropertySimple prop = pc.getSimple(JMXDiscoveryComponent.ADDITIONAL_CLASSPATH_ENTRIES);
if (prop == null || prop.getStringValue() == null || prop.getStringValue().trim().length() == 0) {
return;
}
String[] paths = prop.getStringValue().trim().split(",");
if (paths == null || paths.length == 0) {
return;
}

// get the current set of class path entries - we are going to add to these
List entries = settings.getClassPathEntries();
if (entries == null) {
entries = new ArrayList();
}

// Get all additional classpath entries which can be listed as jar filenames or directories.
// If a directory has "/*.jar" at the end, all jar files found in that directory will be added
// as class path entries.
final class JarFilenameFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
return name.endsWith(".jar");
}
}

for (String path : paths) {
path = path.trim();
if (path.length() > 0) {
if (path.endsWith("*.jar")) {
path = path.substring(0, path.length() - 5);
File dir = new File(path);
File[] jars = dir.listFiles(new JarFilenameFilter());
if (jars != null && jars.length > 0) {
entries.addAll(Arrays.asList(jars));
}
} else {
File pathFile = new File(path);
entries.add(pathFile);
}
}
}

// now that we've appended our additional jars, tell the connection settings about the new list
settings.setClassPathEntries(entries);

return;
}
}

Also, download MC4J libraries patch it with the changed class below and replace it inside JMX plugin after it is built (sincerely as I don’t use maven I don’t know a cleaner way of replacing it prior to build).
Below the changed code for WebsphereConnectionProvider:

/*
* Copyright 2002-2004 Greg Hinkle
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.mc4j.ems.impl.jmx.connection.support.providers;

import org.mc4j.ems.impl.jmx.connection.support.providers.proxy.GenericMBeanServerProxy;

import javax.management.MBeanServer;
import javax.management.j2ee.Management;
import javax.naming.Context;
import javax.naming.NamingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URI;
import java.security.Security;
import java.util.Properties;

/**
* This Node acts as a connection to a WebSphere(tm) MBean Server (TMX4J based).
*
* @author Greg Hinkle (ghinkle@users.sourceforge.net), January 2004
* @version $Revision: 575 $($Author: ghinkl $ / $Date: 2006-05-21 23:38:53 -0300 (Dom, 21 Mai 2006) $)
*/
public class WebsphereConnectionProvider extends AbstractConnectionProvider {

protected GenericMBeanServerProxy statsProxy;
protected MBeanServer mbeanServer;
private Management mejb;

private static final String MEJB_JNDI = "ejb/mgmt/MEJB";

protected void doConnect() throws Exception {
Context ctx = null;

ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();

Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());

try {

//System.setProperty("jmx.serial.form", "1.0");

/* From a WS admin article
Properties clientProps = new Properties();
connectProps.setProperty(AdminClient.CONNECTOR_TYPE,
AdminClient.CONNECTOR_TYPE_SOAP);
connectProps.setProperty(AdminClient.CONNECTOR_HOST, "localhost");
connectProps.setProperty(AdminClient.CONNECTOR_PORT, "8879");

AdminClient adminClient = null;
try
{
adminClient = AdminClientFactory.createAdminClient(clientProps);
}
catch (ConnectorException e)
{
System.out.println("Exception creating admin client: " + e);
}
*/

Class adminClientClass =
Class.forName("com.ibm.websphere.management.AdminClient", true, this.getClass().getClassLoader());
Class adminClientFactoryClass =
Class.forName("com.ibm.websphere.management.AdminClientFactory");

// TODO GH: LATEST! This works from a SUN VM...
// Autodetect the vm and suggest the correct factory
// Hashtable env = new Hashtable();
// env.put(Context.INITIAL_CONTEXT_FACTORY,
// "com.sun.jndi.cosnaming.CNCtxFactory");
// env.put(Context.PROVIDER_URL, "corbaname:iiop:localhost:2809/NameServiceServerRoot");
//env.put(Context.PROVIDER_URL, "iiop://localhost:2809/NameServiceServerRoot");
// ctx = new InitialContext(env);
//this.mejb = retrieveMEJB(ctx);

/*
Properties orbprops = new Properties();
orbprops .put("org.omg.CORBA.ORBClass", "com.ibm.CORBA.iiop.ORB");
orbprops .put("com.ibm.CORBA.ORBInitRef.NameService",
"corbaloc:iiop:localhost:2809/NameService");
orbprops .put("com.ibm.CORBA.ORBInitRef.NameServiceServerRoot",
"corbaloc:iiop:localhost:2809/NameServiceServerRoot");
ORB _orb = ORB.init((String[])null, orbprops );

org.omg.CORBA.Object obj = _orb.resolve_initial_references("NameService");
NamingContextExt initCtx = NamingContextExtHelper.narrow(obj);
Object objref = initCtx.resolve_str("java:comp/env/ejb/mgmt/MEJB");
ManagementHome home =
(ManagementHome)PortableRemoteObject.narrow(objref,ManagementHome.class);
this.mejb = home.create();
*/

//props.put(Context.SECURITY_PRINCIPAL, connectionSettings.getPrincipal());
//props.put(Context.SECURITY_CREDENTIALS, connectionSettings.getCredentials());

Properties props = new Properties();
URI serverUrl = new URI(connectionSettings.getServerUrl());

if (serverUrl.getScheme().equalsIgnoreCase("http") || serverUrl.getScheme().equalsIgnoreCase("https")) {
System.setProperty("javax.net.debug", "ssl,handshake,data,trustmanager");
//Security.addProvider(new sun.security.provider.Sun());
System.setProperty("java.protocol.handler.pkgs","com.sun.net.ssl.internal.www.protocol");
//System.setProperty("ssl.SocketFactory.provider", "javax.net.ssl.SSLSocketFactory");
props.put(
getConstant(adminClientClass, "CONNECTOR_TYPE"),
getConstant(adminClientClass, "CONNECTOR_TYPE_SOAP"));
} else {
props.put(
getConstant(adminClientClass, "CONNECTOR_TYPE"),
getConstant(adminClientClass, "CONNECTOR_TYPE_RMI"));
}
String username = connectionSettings.getPrincipal();
String password = connectionSettings.getCredentials();
boolean security = ((username != null) && (!"".equals(username)));
if (security) {
props.setProperty(getConstant(adminClientClass, "CONNECTOR_SECURITY_ENABLED"), Boolean.toString(security));
props.setProperty(getConstant(adminClientClass, "USERNAME"), username);
props.setProperty(getConstant(adminClientClass, "PASSWORD"), password);
}

props.put(
getConstant(adminClientClass, "CONNECTOR_HOST"),
serverUrl.getHost());
props.put(
getConstant(adminClientClass, "CONNECTOR_PORT"),
String.valueOf(serverUrl.getPort()));

Method createMethod =
adminClientFactoryClass.getMethod("createAdminClient", Properties.class);

Object adminClient =
createMethod.invoke(null, props);

this.statsProxy = new GenericMBeanServerProxy(adminClient);
this.mbeanServer = statsProxy.buildServerProxy();

//this.mejb = retrieveMEJB(ctx);

// TODO GH: Customize exception and error messages to help
// with typical problems (jsse jars missing, passwords, etc.)
} finally {
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
}

public String getConstant(Class clazz, String name) throws Exception {
Field field = clazz.getField(name);
return (String) field.get(null);
}

// public Management getMEJB() {
// return mejb;
// }

private Management retrieveMEJB(Context ic) {
try {
java.lang.Object objref = ic.lookup(MEJB_JNDI);
// ManagementHome home =
// (ManagementHome)PortableRemoteObject.narrow(objref,ManagementHome.class);
// Management mejb = home.create();
return mejb;
} catch(NamingException ne) {
// ErrorManager.getDefault().notify(ne);
// } catch(RemoteException re) {
// ErrorManager.getDefault().notify(re);
} catch(Exception ce) {
// ErrorManager.getDefault().notify(ce);
}
return null;
}
public MBeanServer getMBeanServer() {
return this.mbeanServer;
}

public long getRoundTrips() {
return statsProxy.getRoundTrips();
}

public long getFailures() {
return statsProxy.getFailures();
}

}

Now, since the changes I made to the JMX plugin still don’t cover adding WebSphere libs to classpath it still necessary to add it manually, so copy the following jars from WebSphere to the Agent lib directory:

  • AppServer/lib/bootstrap.jar
  • AppServer/deploytool/itp/plugins/com.ibm.websphere.v61_6.1.200/ws_runtime.jar
  • AppServer/runtimes/com.ibm.ws.admin.client_6.1.0.jar
  • AppServer/plugins/com.ibm.ws.security.crypto_6.1.0.jar

Now the last part, the JVM that is running your agent needs the server certificate for the SOAP Admin port of your WebSphere installation. If you are using Firefox browse to your server on Admin Port you can check it clicking on the server instance under “Application Servers” and then expanding the Ports element and looking for the “SOAP_CONNECTOR_ADDRESS” entry. It is usually 8880. Double click on the padlock icon located on the bottom right corner, click display certificate then export it using the default format (X.509 Certificate (PEM)). Remember where you saved it for the next step.
Now run the following command (pointing to the JVM that will run your agent):

%JAVA_HOME%\jre\bin\keytool -import -alias somealias -file c:/temp/yourcertificate.crt -keystore %JAVA_HOME%\jre\lib\security\cacerts

Replace the %JAVA_HOME% with your JVM directory (or use the command as is in case the environment variable is set).

Now you are ready to setup the component inside JOPR.
For those that don’t want to build it manually, I’m providing the patched rhq-jmx-plugin-1.1.0.GA.jar attached in this PDF.

Note that the steps cited above for the certificate also applies if you are using IBM Tivoli Monitoring 6.2 (fp1 and onwards) together with a custom agent built using Tivoli Agent Builder. If you miss those steps you’ll see security error messages on the JMX agent log.

23
Jan
09

WebSphere UOW TransactionManagerLookup for Hibernate

For those interested in experimenting the latest Transaction Management API from WebSphere (available on newer 6.0 and 6.1 versions), I’ll place the code here of a draft implementation I made (partly based on original WebSphereExtendedTransactionLookup, WebSphere documentations [1][2][3][4] and Spring’s Transaction Manager for WAS).
I hope someday it gets integrated into Hibernate mainstream code but while it isn’t I’ll place here so more people may test it and improve it (reducing the burden over Hibernate team).

package org.hibernate.transaction;

import java.lang.reflect.Method;
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.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAResource;

import org.hibernate.HibernateException;
import org.hibernate.util.NamingHelper;
import org.omg.CORBA.SystemException;

/**
 * @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 TransactionManagerAdapter(props);
	}

	public String getUserTransactionName() {
		return "java:comp/UserTransaction";
	}

	public static class TransactionManagerAdapter implements TransactionManager {

		private final Object uowManager;
		private final Class uowManagerClass;
		private final Object uowSynchronizationRegistry;
		private final Class uowSynchronizationRegistryClass;
		private final Method registerSynchronizationMethod;
		private final Method setRollbackOnlyMethod;

		private final Properties properties;
		private final Method getLocalIdMethod;

		private TransactionManagerAdapter(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[] {}
					);

				uowSynchronizationRegistry = NamingHelper.getInitialContext(props).lookup( UOW_SYNCHRONIZATION_MANAGER_JNDI );
				uowSynchronizationRegistryClass = Class.forName("com.ibm.websphere.uow.UOWSynchronizationRegistry");

				registerSynchronizationMethod = uowSynchronizationRegistryClass.getMethod(
						"registerInterposedSynchronization",
						new Class[] { Synchronization.class }
					);
				Class extendedJTATransactionClass = Class.forName("com.ibm.websphere.jtaextensions.ExtendedJTATransaction");
				getLocalIdMethod = extendedJTATransactionClass.getMethod( "getLocalId", null );

			}
			catch (ClassNotFoundException cnfe) {
				throw new HibernateException(cnfe);
			}
			catch (NoSuchMethodException nsme) {
				throw new HibernateException(nsme);
			} catch (NamingException ne) {
				throw new HibernateException(ne);
			}
		}

		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 TransactionAdapter(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 (Exception e) {
				throw new HibernateException(e);
			}
		}

		public void setTransactionTimeout(int i) throws SystemException {
			throw new UnsupportedOperationException();
		}

		public Transaction suspend() throws SystemException {
			throw new UnsupportedOperationException();
		}

		public class TransactionAdapter implements Transaction {

			private final Object extendedJTATransaction;

			private TransactionAdapter(Properties props) {
				try {
					extendedJTATransaction = NamingHelper.getInitialContext(props)
						.lookup("java:comp/websphere/ExtendedJTATransaction");

				}
				catch (NamingException ne) {
					throw new HibernateException(ne);
				}
			}

			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 TransactionAdapter) ) return false;
				TransactionAdapter that = (TransactionAdapter) other;
				return getLocalId().equals( that.getLocalId() );
			}

			private Object getLocalId() {
				try {
					return getLocalIdMethod.invoke(extendedJTATransaction, null);
				}
				catch (Exception e) {
					throw new HibernateException(e);
				}
			}

			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);
				}
			}
		}

	}

}

Please, if you use it and find any bugs, comments or suggestions, leave a comment on this post.

Update:

I’ve recently improved this implementation to support the Isolater.Delegate interface on WebSphere anyone interested please take a look at this post.

10
Dec
08

Websphere PMI: enabling and viewing data

For those who ever needed to have a deeper look at application internals that may be impacting performance probably had this impression:

  • System.out.println with System.nanoTime (or currentTimeMillis) is tedious, errorprone and limited
  • A profiler is an overkill not to mention cumbersome (and unavailable for certain platforms [eg.:tptp on AIX]*)
  • This is the scenario where Websphere PMI is a killer feature.

    Imagine that your application isn’t performing as expected. Many can be the reasons for the poor performance. I’ve faced myself a scenario where the application was waiting a long time for getting a JMS connection from Websphere internal provider since its default configuration of 10 connections maximum isn’t acceptable for any application with performance requirements of even 100 transactions per second.

    Enabling PMI

    By default, Websphere 6.1 ND comes with basic PMI metrics enabled. These include for example:

    • Enterprise Beans.Create Count
    • JDBC Connection Pools.Wait Time
    • JDBC Connection Pools.Use Time

    If you need anything more than the default, you can change under:

    Monitoring and Tuning > Performance Monitoring Infrastructure (PMI)

    then click on the desired server.

    After you have chosen the desired metrics (remember that more metrics involve more CPU impact on runtime), go to the following menu:

    Monitoring and Tuning > Performance Viewer > Current Activity

    Now you need to check if your server is in fact already collecting data, if it is already enabled but not collecting, Collection Status will show Available. In order to start collecting, check the desired server and click Start Monitoring button. After clicking the button it will now show Monitored on the status column.

    Now you can click on the desired server and tick for example one of your connection pools under the tree on the left, you should see an structure similar to the below:

    Performance Modules > JDBC Connection Pools > Oracle XA Provider > yourDataSource

    After clicking the metric you’ll have a graph display of the current data and also a tabular with the snapshot of the indicator below.

    * note: Eclipse TPTP is said to be supported on AIX on version 4.3.1 but I have not been able to make it work




    ClustrMaps

    Blog Stats

    • 375,197 hits since aug'08

    %d bloggers like this: