In the process of getting Middlegen's airline sample to work (sort of, except for Struts), I developed a new SessionFactoryObjectFactory which actually works with Tomcat's JNDI resources. None of the stuff in the docs or found from google were satisfactory, e.g. http://www.hibernate.org/114.html http://www.hibernate.org/133.html http://www.hibernate.org/105.html altho they provided good starting points.
Instead I extended/finished net.sf.hibernate.impl.SessionFactoryObjectFactory (is that finished/does it really work somewhere?). I changed getObjectInstance() to call a method createInstance() if the factory does not exist. I called my class BasicSessionFactoryObjectFactory since I started by looking at what org.apache.commons.dbcp.BasicDataSourceFactory does. And I'm not sure what my changes would do to other containers. BasicSessionFactoryObjectFactory will configure hibernate from individual ResourceParms, or from a configFilePath specified as a ResourceParm. I've included rather than attached the files below (no diffs so you can play with both ObjectFactory's). Code bits were copied from/inspired by BasicDataSourceFactory, hibernate's Configuration, HibernateService classes, and the JndiSessionFactoryImpl in the docs. I still have one "XXX" spot that I don't what to do with - which properties to send to addInstance(). Also, I know that SessionFactoryImpl ends up calling the original SessionFactoryObjectFactory at some points, but this seems to work. oh, I'm using Hibernate 2.1 and Tomcat 5.0.18. I'd appreciate any comments/suggestions/corrections. (I also briefly explored getting MBeans to work with Tomcat but this looked easier to understand/get working. Basically I added a call to NamingHelper.bind() to HibernateService but then I ran into other JMX problems and switched to ObjectFactory.) John Allison [EMAIL PROTECTED] Here's what I put in the Context element of my webapp .xml file: <Resource name="AirlineHibernateFactory" type="net.sf.hibernate.SessionFactory"/> <ResourceParams name="AirlineHibernateFactory"> <parameter> <name>factory</name> <value>airline.BasicSessionFactoryObjectFactory</value> </parameter> <!-- configFilePath is relative to "/WEB-INF/classes" and must start with '/' --> <!-- if configFilePath is specified, then it's used and the other parameters are ignored --> <!-- <parameter> <name>configFilePath</name> <value>/hibernate.cfg.xml</value> </parameter> --> <!-- MapResources is ',' separated list of .hbm.xml files, relative to "/WEB-INF/classes" and each must start with '/' --> <parameter> <name>MapResources</name> <value>/airline/hibernate/Flight.hbm.xml,/airline/hibernate/Reservation.hbm.xml,/airline/hibernate/Person.hbm.xml</value> </parameter> <parameter> <name>connection.datasource</name> <value>java:comp/env/jdbc/airline</value> </parameter> <parameter> <name>dialect</name> <value>net.sf.hibernate.dialect.MySQLDialect</value> </parameter> <parameter> <name>use_outer_join</name> <value>true</value> </parameter> <parameter> <name>show_sql</name> <value>true</value> </parameter> <parameter> <name>transaction.factory_class</name> <value>net.sf.hibernate.transaction.JDBCTransactionFactory</value> </parameter> </ResourceParams> And here's the Java source, which I compiled and put under /WEB-INF/classes: //$Id$ package airline; import java.util.Hashtable; import java.util.Iterator; import java.util.Properties; import java.util.Enumeration; import javax.naming.Context; import javax.naming.InvalidNameException; import javax.naming.Name; import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.RefAddr; import javax.naming.event.EventContext; import javax.naming.event.NamespaceChangeListener; import javax.naming.event.NamingEvent; import javax.naming.event.NamingExceptionEvent; import javax.naming.event.NamingListener; import javax.naming.spi.ObjectFactory; import net.sf.hibernate.SessionFactory; import net.sf.hibernate.util.FastHashMap; import net.sf.hibernate.util.NamingHelper; import net.sf.hibernate.HibernateException; import net.sf.hibernate.cfg.Configuration; import net.sf.hibernate.cfg.Environment; import net.sf.hibernate.util.PropertiesHelper; import net.sf.hibernate.MappingException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Resolves <tt>SessionFactory</tt> JNDI lookups and deserialization * @see net.sf.hibernate.impl.SessionFactoryObjectFactory * @see org.apache.commons.dbcp.BasicDataSourceFactory * @see net.sf.hibernate.cfg.Configuration; * @author John Allison - finished implementation by actually creating SessionFactory's */ public class BasicSessionFactoryObjectFactory implements ObjectFactory { private static final BasicSessionFactoryObjectFactory INSTANCE; //to stop the class from being unloaded private static final Log log; static { log = LogFactory.getLog(BasicSessionFactoryObjectFactory.class); INSTANCE = new BasicSessionFactoryObjectFactory(); log.debug("initializing class BasicSessionFactoryObjectFactory"); } private static final FastHashMap INSTANCES = new FastHashMap(); private static final FastHashMap NAMED_INSTANCES = new FastHashMap(); private static final NamingListener LISTENER = new NamespaceChangeListener() { public void objectAdded(NamingEvent evt) { log.debug( "A factory was successfully bound to name: " + evt.getNewBinding().getName() ); } public void objectRemoved(NamingEvent evt) { String name = evt.getOldBinding().getName(); log.info("A factory was unbound from name: " + name); Object instance = NAMED_INSTANCES.remove(name); Iterator iter = INSTANCES.values().iterator(); while ( iter.hasNext() ) { if ( iter.next()==instance ) iter.remove(); } } public void objectRenamed(NamingEvent evt) { String name = evt.getOldBinding().getName(); log.info("A factory was renamed from name: " + name); NAMED_INSTANCES.put( evt.getNewBinding().getName(), NAMED_INSTANCES.remove(name) ); } public void namingExceptionThrown(NamingExceptionEvent evt) { log.warn( "Naming exception occurred accessing factory: " + evt.getException() ); } }; private static String[] parseResourceList(String resourceList) { return PropertiesHelper.toStringArray(resourceList, " ,\n\t\r\f"); } public Object getObjectInstance(Object obj, Name name, Context ctx, Hashtable env) throws Exception { log.error("getObjectInstance: " + obj + " " + name); if ((obj == null) || !(obj instanceof Reference)) { return (null); } Reference reference = (Reference) obj; if (!"net.sf.hibernate.SessionFactory".equals(reference.getClassName())) { return (null); } log.warn("JNDI lookup: " + name); String uid = (String) reference.get(0).getContent(); Object instance = getInstance(uid); if (instance == null) { try { instance = createInstance(reference,name,ctx,env); } catch (HibernateException he) { log.error( "Could not build SessionFactory: " + he.getMessage() ); log.debug("Error was", he); return null; } // XXX Properties : use env ? or something from ctx ? addInstance(uid,name.toString(),(SessionFactory)instance,new Properties()); } return instance; } public static SessionFactory createInstance(Reference reference, Name name, Context ctx, Hashtable env) throws Exception { SessionFactory sf = null; RefAddr ra = null; log.info("createInstance for " + name); Configuration cf = new Configuration(); if (cf == null) { throw new HibernateException("can't allocate new Configuration"); } ra = reference.get("configFilePath"); if (ra != null) { Object value = ra.getContent(); if (value == null) { throw new HibernateException("configFilePath content is null"); } String resource = value.toString(); log.info("configFilePath="+resource); if (resource.length() == 0) { throw new HibernateException("configFilePath parameter is empty"); } return cf.configure(resource).buildSessionFactory(); } // populate properties from ResourceParams // don't want to directly cf.setProperty() so that we can fiddle with mapresources and also check for empty and fall-thru to cf.configure() Properties props = new Properties(); props.setProperty(Environment.SESSION_FACTORY_NAME,name.toString()); for (Enumeration e = reference.getAll() ; e.hasMoreElements() ;) { ra = (RefAddr)e.nextElement(); String type = ra.getType().toString().trim(); // do I need trim()? String cont = ra.getContent().toString().trim(); if (!type.equalsIgnoreCase("factory") && !type.equalsIgnoreCase("scope")) { // skip factory, that's this class; scope is for jndi props.setProperty(type,cont); if ( !type.startsWith("hibernate") ) props.setProperty("hibernate." + type, cont); } } // add MapResources to Configuration individually String mapres = (String)props.remove("MapResources"); if (mapres != null) { String[] mappingFiles = parseResourceList(mapres); for ( int i=0; i<mappingFiles.length; i++ ) { try { cf.addResource( mappingFiles[i], Thread.currentThread().getContextClassLoader() ); } catch (MappingException me) { cf.addResource( mappingFiles[i], Environment.class.getClassLoader() ); } } } if (!props.isEmpty()) { log.info("configuring hibernate with properties from ResourceParams"); log.info("properties = " + props); cf.addProperties(props); // don't use setProperties() so we can keep the defaults for properties not specified as ResourceParms return cf.buildSessionFactory(); } // fall-thru and attempt default configuration log.info("configuring hibernate with default"); return cf.configure().buildSessionFactory(); } public static void addInstance(String uid, String name, SessionFactory instance, Properties properties) { log.debug("registered: " + uid + " (" + ( (name==null) ? "unnamed" : name ) + ')'); INSTANCES.put(uid, instance); if (name!=null) NAMED_INSTANCES.put(name, instance); //must add to JNDI _after_ adding to HashMaps, because some JNDI servers use serialization if (name==null) { log.info("no JNDI name configured"); } else { log.info("Factory name: " + name); try { Context ctx = NamingHelper.getInitialContext(properties); NamingHelper.bind(ctx, name, instance); log.info("Bound factory to JNDI name: " + name); ( (EventContext) ctx ).addNamingListener(name, EventContext.OBJECT_SCOPE, LISTENER); } catch (InvalidNameException ine) { log.error("Invalid JNDI name: " + name, ine); } catch (NamingException ne) { log.warn("Could not bind factory to JNDI", ne); } catch(ClassCastException cce) { log.warn("InitialContext did not implement EventContext"); } } } public static void removeInstance(String uid, String name, Properties properties) { //TODO: theoretically non-threadsafe... if (name!=null) { log.info("Unbinding factory: " + name); try { Context ctx = NamingHelper.getInitialContext(properties); ctx.unbind(name); log.info("Unbound factory from JNDI name: " + name); } catch (InvalidNameException ine) { log.error("Invalid JNDI name: " + name, ine); } catch (NamingException ne) { log.warn("Could not unbind factory from JNDI", ne); } NAMED_INSTANCES.remove(name); } INSTANCES.remove(uid); } public static Object getNamedInstance(String name) { log.warn("lookup: name=" + name); Object result = NAMED_INSTANCES.get(name); if (result==null) { log.warn("Not found: " + name); log.warn(NAMED_INSTANCES); } return result; } public static Object getInstance(String uid) { log.debug("lookup: uid=" + uid); Object result = INSTANCES.get(uid); if (result==null) { log.warn("Not found: " + uid); log.debug(INSTANCES); } return result; } } //end of file ------------------------------------------------------- This SF.Net email is sponsored by: IBM Linux Tutorials Free Linux tutorial presented by Daniel Robbins, President and CEO of GenToo technologies. Learn everything from fundamentals to system administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click _______________________________________________ hibernate-devel mailing list [EMAIL PROTECTED] https://lists.sourceforge.net/lists/listinfo/hibernate-devel