A generic BeanFactory class for custom JNDI resource

In my previous 3 posts(post 1, post 2, and post 3, I wrote about how to create, inject, look up, and configure a custom JNDI resource in glassfish application server. Usually a custom JNDI resource type conform to JavaBeans conventions, with property fields, getters and setters. Therefore, we should be able to write a generic bean factory class that works with any such custom JNDI resource types.

Just to recap, to create a custom JNDI resource, we need at least 2 classes: (1) the resource class to represent your resource, and (2) the factory class that knows how to create instances of your resource. The factory class must implement interface javax.naming.spi.ObjectFactory, which has a single method:


public Object getObjectInstance(Object obj,
                         Name name,
                         Context nameCtx,
                         Hashtable<?,?> environment)
                         throws Exception

Actually, there is already such a bean factory class org/apache/naming/factory/BeanFactory.class in glassfish lib/appserv-rt.jar. Its source code can be viewed here. But I found I have to modify 1 lines to make it work, otherwise the lookup or injection result is always null.


diff -w -u -b -r1.3 BeanFactory.java
--- BeanFactory.java    6 Nov 2006 21:14:08 -0000       1.3
+++ BeanFactory.java    9 Mar 2007 18:09:56 -0000
@@ -121,7 +121,7 @@
                                     Hashtable environment)
         throws NamingException {

-        if (obj instanceof ResourceRef) {
+        if (obj instanceof Reference) {
For your convenience, the modified generic object factory class is copied below:
package customjndi;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import javax.naming.Reference;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.spi.ObjectFactory;

public class GenericObjectFactory implements ObjectFactory {
    private static final Logger logger = Logger.getLogger(GenericObjectFactory.class.getName());

    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
	    Hashtable environment)
	    throws NamingException {
	logger.fine("obj=" + obj);
	logger.fine("name=" + name);
	logger.fine("nameCtx=" + nameCtx);
	if (obj instanceof Reference) {
	    try {
		Reference ref = (Reference) obj;
		String beanClassName = ref.getClassName();
		Class beanClass = null;
		ClassLoader tcl =
			Thread.currentThread().getContextClassLoader();
		if (tcl != null) {
		    try {
			beanClass = tcl.loadClass(beanClassName);
		    } catch (ClassNotFoundException e) {
		    }
		} else {
		    try {
			beanClass = Class.forName(beanClassName);
		    } catch (ClassNotFoundException e) {
			e.printStackTrace();
		    }
		}
		if (beanClass == null) {
		    throw new NamingException("Class not found: " + beanClassName);
		}

		BeanInfo bi = Introspector.getBeanInfo(beanClass);
		PropertyDescriptor[] pda = bi.getPropertyDescriptors();

		Object bean = beanClass.newInstance();

		Enumeration e = ref.getAll();
		while (e.hasMoreElements()) {

		    RefAddr ra = (RefAddr) e.nextElement();
		    String propName = ra.getType();

		    String value = (String) ra.getContent();

		    Object[] valueArray = new Object[1];

		    int i = 0;
		    for (i = 0; i < pda.length; i++) {

			if (pda[i].getName().equals(propName)) {

			    Class propType = pda[i].getPropertyType();

			    if (propType.equals(String.class)) {
				valueArray[0] = value;
			    } else if (propType.equals(Character.class) || propType.equals(char.class)) {
				valueArray[0] = Character.valueOf(value.charAt(0));
			    } else if (propType.equals(Byte.class) || propType.equals(byte.class)) {
				valueArray[0] = Byte.valueOf(value);
			    } else if (propType.equals(Short.class) || propType.equals(short.class)) {
				valueArray[0] = Short.valueOf(value);
			    } else if (propType.equals(Integer.class) || propType.equals(int.class)) {
				valueArray[0] = Integer.valueOf(value);
			    } else if (propType.equals(Long.class) || propType.equals(long.class)) {
				valueArray[0] = Long.valueOf(value);
			    } else if (propType.equals(Float.class) || propType.equals(float.class)) {
				valueArray[0] = Float.valueOf(value);
			    } else if (propType.equals(Double.class) || propType.equals(double.class)) {
				valueArray[0] = Double.valueOf(value);
			    } else {
				throw new NamingException("String conversion for property type '" + propType.getName() + "' not available");
			    }

			    Method setProp = pda[i].getWriteMethod();
			    if (setProp != null) {
				setProp.invoke(bean, valueArray);
			    } else {
				throw new NamingException("Write not allowed for property: " + propName);
			    }

			    break;

			}

		    }

		    if (i == pda.length) {
			throw new NamingException("No set method found for property: " + propName);
		    }

		}

		return bean;

	    } catch (java.beans.IntrospectionException ie) {
		throw new NamingException(ie.getMessage());
	    } catch (java.lang.IllegalAccessException iae) {
		throw new NamingException(iae.getMessage());
	    } catch (java.lang.InstantiationException ie2) {
		throw new NamingException(ie2.getMessage());
	    } catch (java.lang.reflect.InvocationTargetException ite) {
		throw new NamingException(ite.getMessage());
	    }

	} else {
	    return null;
	}

    }
}

For more details, see glassfish dev email alias discuss on Factory class for custom JNDI resource

Comments:

Hello,

I wonder whether the above issues with the ResourceRef vs. Reference is not fixed in the Glassfish code.

Did you file a bug?

How can I debug such custom resource deployment?
Actually, I want to create a MQ Series JMS ConnectionFactory
using the IBM class com.ibm.mqjms.MQConnectionFactoryFactory which implements the above ObjectFactory interface.

But my client just gets:

java.lang.ClassCastException: javax.naming.Reference cannot be cast to javax.jms.QueueConnectionFactory

Any idea?

Many thanks for your blog. This actually showed me the correct way to deploy the MQ admin objects after days and weeks of research.

Best regards, Jörg Thönnes

Posted by Jörg Thönnes on November 27, 2008 at 10:17 PM EST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Cheng Fang

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today