The following discussion outlines specific configurations and sample code that allow OSWorkflow and the PropertySet projects to connect with the Spring Framework (version 1.2.7) and Hibernate (version 3.x). While your specific configuration may be different than what is listed below the general concepts should be the same.
Data Access
First, we will cover the data access problem and how to get the ProperySet integrated with Spring and Hibernate. Following I have listed a data access interface and hibernate implementation class that follows along the lines of DI within Spring. The property set interface:
public interface IHibernatePropertySetDAO extends IDAO
{
/**
* Save the implementation of a PropertySetItem.
*
* @param item
* @param isUpdate
* Boolean indicating whether or not this item already exists
*/
void setImpl(PropertySetItem item, boolean isUpdate);
/**
* Return a list of property set objects
* @param entityName - name of property set entity
* @param entityId - id of property set
* @param prefix - parameter added to selection query
* @param type - property set key_type
* @return Collection
*/
Collection getKeys(String entityName, Long entityId, String prefix, int type);
/**
* Return a new PropertySetItem; this is object construction only.
* @param entityName - name of property set entity
* @param entityId - id for property set
* @param key - key
* @return - PropertySetItem
*/
PropertySetItem create(String entityName, long entityId, String key);
/**
* Return a PropertySetItem from persistent storage. Since there is a composite
* key on the os_propertyset table the selection requires the entityName, entityId, and the
* key.
* @param entityName - entity name
* @param entityId - the entity id
* @param key - the key
* @return PropertySetItem
*/
PropertySetItem findByKey(String entityName, Long entityId, String key);
/**
* Remove a PropertySetItem from persistent storage
* @param entityName - entity name
* @param entityId - entity id
* @param key - the key
*/
void remove(String entityName, Long entityId, String key);
/**
* Remove a PropertySetItem from persistent storage
* @param entityName - entity name
* @param entityId - enting id
*/
void remove(String entityName, Long entityId);
}
As you can see this interface extends IDAO where IDAO is a marker interface within the your object model that identifies this interface as a data access interface. Now for the implementation class (this is similar to the class that is provided with the project however has specifics for Spring):
public class HibernatePropertySetDAOImpl extends HibernateDaoSupport implements IHibernatePropertySetDAO
{
private Log log = LogFactory.getLog(HibernatePropertySetDAOImpl.class);
/**
* Default construstor
*/
public HibernatePropertySetDAOImpl()
{
super();
}
/**
* @see com.tricision.maas.dao.IHibernatePropertySetDAO#setImpl(PropertySetItem, boolean)
*/
public void setImpl(PropertySetItem item, boolean isUpdate)
{
this.log.debug("TOP of setImpl... ");
try
{
this.log.debug("(setImpl) Have an open sessionFactory");
if( isUpdate )
{
this.log.debug("(setImpl) Attempting update"); getHibernateTemplate().update(item);
}
else
{
this.log.debug("(setImpl) Attempting save"); getHibernateTemplate().save(item);
}
getHibernateTemplate().flush();
this.log.debug("(setImpl) Attempting flush"); }
catch( HibernateException he )
{
throw new PropertyException("Could not save key '" + item.getKey() + "':" + he.getMessage()); }
}
/**
* @see com.tricision.maas.dao.IHibernatePropertySetDAO#getKeys(java.lang.String, java.lang.Long, java.lang.String, int)
*/
public Collection getKeys(String entityName, Long entityId, String prefix,
int type)
{
this.log.debug("Top of getKeys() with entityName: " + entityName); List list = null;
try
{
list = getKeysImpl(entityName, entityId, prefix, type);
}
catch( HibernateException e )
{
list = Collections.EMPTY_LIST;
}
return list;
}
/**
* @see com.tricision.maas.dao.IHibernatePropertySetDAO#create(String, long, String)
*/
public PropertySetItem create(String entityName, long entityId, String key)
{
return new PropertySetItemImpl(entityName, entityId, key);
}
/**
* @see com.tricision.maas.dao.IHibernatePropertySetDAO#findByKey(String, Long, String)
*/
public PropertySetItem findByKey(String entityName, Long entityId, String key)
{
this.log.debug(this.getClass().getName() + " Top of findByKey with entityName: " + entityName + " entityId: " + entityId + " and Key: " + key); PropertySetItem item = null;
try
{
this.log.debug("Looking for propertysetitem: " + entityName + " with entityId: " + entityId + " and key: " + key); item = getItem(entityName, entityId, key);
}
catch( HibernateException e )
{
this.log.error("Item not found in persistent storage. " + e.getMessage()); return null;
}
return item;
}
/**
* @see com.tricision.maas.dao.IHibernatePropertySetDAO#remove(String, Long)
*/
public void remove(String entityName, Long entityId)
{
try
{
Collection keys = getKeys(entityName, entityId, null, 0);
Iterator iter = keys.iterator();
while( iter.hasNext() )
{
String key = (String) iter.next();
getHibernateTemplate().delete( getItem(entityName, entityId, key));
}
}
catch( HibernateException e )
{
throw new PropertyException("Could not remove all keys: " + e.getMessage()); }
}
/**
* @see com.tricision.maas.dao.IHibernatePropertySetDAO#remove(String, Long, String)
*/
public void remove(String entityName, Long entityId, String key)
{
try
{
getHibernateTemplate().delete(getItem(entityName, entityId, key));
}
catch( HibernateException e )
{
throw new PropertyException("Could not remove key '" + key + "': " + e.getMessage());
}
}
/**
* Get the propertyset item from persistent storage.
* @param entityName - the name of the entity
* @param entityId - the id of the entity
* @param key - the string that is the key for the entity
* @return - PropertySetItem
* @throws HibernateException
*/
public PropertySetItem getItem(final String entityName, final Long entityId, final String key) throws HibernateException
{
/**
* Propertly construct the object before the find.
*/
PropertySetItemImpl findItem = new PropertySetItemImpl();
findItem.setEntityId(entityId.longValue());
findItem.setKey(key);
findItem.setEntityName(entityName);
return (PropertySetItem) getHibernateTemplate().get(PropertySetItemImpl.class,findItem);
}
/**
* This is the body of the getKeys() method, so that you can reuse it
* wrapped by your own session management.
* @param entityName
* @param entityId
* @param prefix
* @param type
* @return
* @throws HibernateException
*/
public List getKeysImpl(String entityName, Long entityId, String prefix, int type) throws HibernateException
{
Query query;
if((prefix != null) && (type > 0))
{
query = getHibernateTemplate().getSessionFactory().openSession().getNamedQuery("all_keys_with_type_like"); query.setString("like", prefix + '%'); query.setInteger("type", type); }
else if(prefix != null)
{
query = getHibernateTemplate().getSessionFactory().openSession().getNamedQuery("all_keys_like"); query.setString("like", prefix + '%'); }
else if(type > 0)
{
query = getHibernateTemplate().getSessionFactory().openSession().getNamedQuery("all_keys_with_type"); query.setInteger("type", type); }
else
{
query = getHibernateTemplate().getSessionFactory().openSession().getNamedQuery("all_keys"); }
query.setString("entityName", entityName); query.setLong("entityId", entityId.longValue()); return query.list();
}
}
Notice this class extends the Spring org.springframework.orm.hibernate3.support.HibernateDaoSupport class and implements the DAO interface. For access to the Hibernate session, this class will use the getHibernateTemplate() method and therefore the Session will be from Spring and not created locally.
PropertySet
What follows is the PropertySet definition for the 'HibernateProperySet' class. This class has been somewhat modified from the class provided with the Opensymphony package:
public class HibernatePropertySet extends AbstractPropertySet
{
protected static Log log = LogFactory.getLog(HibernatePropertySet.class);
private HibernateConfigurationProvider configProvider;
private Long entityId;
private String entityName;
public Collection getKeys(String prefix, int type) throws PropertyException
{
return this.configProvider.getPropertySetDAO().getKeys(this.entityName, this.entityId,
prefix, type);
}
public int getType(String key) throws PropertyException
{
return findByKey(key).getType();
}
public boolean exists(String key) throws PropertyException
{
try
{
if( findByKey(key) != null )
{
return true;
}
return false;
}
catch( PropertyException e )
{
return false;
}
}
@Override
public void init(Map config, Map args)
{
log.debug("(HibernatePropertySet) top of init.");
super.init(config, args);
this.entityId = (Long) args.get("entityId"); this.entityName = (String) args.get("entityName");
this.configProvider = (HibernateConfigurationProvider) args.get("configurationProvider");
/* if we did not get given one in the args, we need to set a config provider up */
if(this.configProvider == null )
{
String configProviderClass = (String) config.get("configuration.provider.class"); if( configProviderClass != null )
{
if( log.isDebugEnabled() )
{
log.debug("Setting up property set provider of class: " + configProviderClass); }
try
{
this.configProvider = (HibernateConfigurationProvider) ClassLoaderUtil
.loadClass(configProviderClass, this.getClass()).newInstance();
}
catch( Exception e )
{
log.error("Unable to load configuration provider class: "+ configProviderClass, e); return;
}
}
else
{
if( log.isDebugEnabled() )
{
log.debug("Setting up property set with DefaultHibernateConfigurationProvider"); }
this.configProvider = new DefaultHibernateConfigurationProvider();
}
this.configProvider.setupConfiguration(config);
}
else
{
if( log.isDebugEnabled() )
{
log.debug("Setting up property set with hibernate provider passed in args."); }
}
}
public void remove(String key) throws PropertyException
{
this.configProvider.getPropertySetDAO().remove(this.entityName, this.entityId, key);
}
public void remove() throws PropertyException
{
this.configProvider.getPropertySetDAO().remove(this.entityName, this.entityId);
}
@Override
public boolean supportsType(int type)
{
switch ( type )
{
case PropertySet.DATA :
case PropertySet.OBJECT :
case PropertySet.PROPERTIES :
case PropertySet.XML :
return false;
}
return true;
}
@Override
protected void setImpl(int type, String key, Object value) throws PropertyException
{
PropertySetItem item = null;
boolean update = true;
item = this.configProvider.getPropertySetDAO().findByKey(this.entityName, this.entityId, key);
if( item == null )
{
update = false;
item = this.configProvider.getPropertySetDAO().create(this.entityName, this.entityId.longValue(), key);
}
else if( item.getType() != type )
{
throw new PropertyException("Existing key '" + key + "' does not have matching type of " + type); }
switch ( type )
{
case BOOLEAN :
item.setBooleanVal(((Boolean) value).booleanValue());
break;
case DOUBLE :
item.setDoubleVal(((Double) value).doubleValue());
break;
case STRING :
case TEXT :
item.setStringVal((String) value);
break;
case LONG :
item.setLongVal(((Long) value).longValue());
break;
case INT :
item.setIntVal(((Integer) value).intValue());
break;
case DATE :
item.setDateVal((Date) value);
break;
default:
throw new PropertyException("type " + type + " not supported"); }
item.setType(type);
this.configProvider.getPropertySetDAO().setImpl(item, update);
}
@Override
protected Object get(int type, String key) throws PropertyException
{
PropertySetItem item = findByKey(key);
if( item == null )
{
return null;
}
if( item.getType() != type )
{
throw new PropertyException("key '" + key + "' does not have matching type of " + type); }
switch ( type )
{
case BOOLEAN :
return new Boolean(item.getBooleanVal());
case DOUBLE :
return new Double(item.getDoubleVal());
case STRING :
case TEXT :
return item.getStringVal();
case LONG :
return new Long(item.getLongVal());
case INT :
return new Integer(item.getIntVal());
case DATE :
return item.getDateVal();
}
throw new PropertyException("type " + type + " not supported"); }
private PropertySetItem findByKey(String key) throws PropertyException
{
return this.configProvider.getPropertySetDAO().findByKey(this.entityName, this.entityId, key);
}
}
PropertySet Configuration
Following are required for the configuration of the PropertySet within Hibernate (so to speak). The DefaultHibernateConfigurationProvider will need to be wired to Spring in order to get a handle on the SessionFactory (more on this later). HibernateConfigurationProvider is the interface that is implemented on DefaultHibernateConfigurationProvider.
public interface HibernateConfigurationProvider
{
/**
* @return Configuration - Hibernate configuration
*/
public Configuration getConfiguration();
/**
* @return IHibernateProperySetDAO
*/
IHibernatePropertySetDAO getPropertySetDAO();
/**
* Setup a Hibernate configuration object with the given properties.
* This will always be called before getConfiguration().
* @param Map - properties to use for the configuration
*/
void setupConfiguration(Map properties);
}
The implementation:
public class DefaultHibernateConfigurationProvider implements
HibernateConfigurationProvider
{
private Configuration configuration;
private IHibernatePropertySetDAO propertySetDAO;
private SessionFactory sessionFactory;
public void setConfiguration(Configuration _configuration)
{
this.configuration = _configuration;
}
public Configuration getConfiguration()
{
return this.configuration;
}
public IHibernatePropertySetDAO getPropertySetDAO()
{
if( this.propertySetDAO == null )
{
HibernatePropertySetDAOImpl hpd = new HibernatePropertySetDAOImpl();
hpd.setSessionFactory(this.sessionFactory);
this.propertySetDAO = hpd;
}
return this.propertySetDAO;
}
public void setSessionFactory(SessionFactory _sessionFactory)
{
this.sessionFactory = _sessionFactory;
}
public void setupConfiguration(Map configurationProperties)
{
try
{
this.configuration = new Configuration()
.addClass(PropertySetItemImpl.class);
Iterator itr = configurationProperties.keySet().iterator();
while( itr.hasNext() )
{
String key = (String) itr.next();
if( key.startsWith("hibernate") ) {
this.configuration.setProperty(key,
(String) configurationProperties.get(key));
}
}
this.sessionFactory = this.configuration.buildSessionFactory();
}
catch( HibernateException e )
{
}
}
}
Finally, the property set delegate:
public class DefaultHibernatePropertySetDelegate implements PropertySetDelegate
{
private SessionFactory sessionFactory=null;
/**
* Default constructor
*/
public DefaultHibernatePropertySetDelegate()
{
super();
}
/**
* Get the propery set
* @param entryId - long, the entry
* @return ProperySet - the propertySet
*/
public PropertySet getPropertySet(long entryId)
{
HashMap <Object,Object>args = new HashMap <Object,Object>();
args.put("entityName", "OSWorkflowEntry"); args.put("entityId", new Long(entryId));
DefaultHibernateConfigurationProvider configurationProvider = new DefaultHibernateConfigurationProvider();
configurationProvider.setSessionFactory(getSessionFactory());
args.put("configurationProvider", configurationProvider);
return PropertySetManager.getInstance("hibernate3", args); }
/**
* @return Returns the sessionFactory.
*/
public SessionFactory getSessionFactory()
{
return this.sessionFactory;
}
/**
* @param _sessionFactory The sessionFactory to set.
*/
public void setSessionFactory(SessionFactory _sessionFactory)
{
this.sessionFactory = _sessionFactory;
}
}
That is all that is required from the code side. Next is the wiring into Spring and also the ProperySet configuration and OSWorklow configuration.
Property Set Configuration (properyset.xml)
The propeertyset.xml file will provide the hibernate3 property with the associated value of the HibernatePropertSet implementation found above; specifically:
<propertysets>
<propertyset name="hibernate3" class="com.tricision.maas.workflow.osworkflow.propertyset.HibernatePropertySet" />
</propertysets>
OSWorkflow Configuration (osworkflow.xml)
The osworkflow.xml file will provide the workflow store and the workflow factory class information; specifically:
<osworkflow>
<persistence class="com.opensymphony.workflow.spi.hibernate3.HibernateWorkflowStore" />
<factory class="com.opensymphony.workflow.loader.URLWorkflowFactory">
<property key="cache" value="true" />
</factory>
</osworkflow>
Spring Wiring (osworkflow-spring.xml)
What follows is the wiring that is required in the Spring context xml files. Your files may be named differently from what is provided, the important thing is the wirings. First the osworkflow wiring into Spring:
<beans>
<!-- ========================= OSWORKFLOW DEFINITIONS ========================= -->
<!-- Hibernate 3 WorkflowStore -->
<bean id="workflowStore" class="com.opensymphony.workflow.spi.hibernate3.SpringHibernateWorkflowStore">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
<property name="propertySetDelegate">
<bean id="propertySetDelegate" class="com.tricision.maas.service.impl.DefaultHibernatePropertySetDelegate">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>
</property>
<property name="cacheable"><value>true</value></property>
</bean>
<!-- Hibernate 3 WorkflowFactory -->
<bean id="workflowFactory" class="com.opensymphony.workflow.spi.hibernate3.SpringHibernateWorkflowFactory" init-method="initDone">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
<property name="reload"><value>false</value></property>
<property name="validate"><value>true</value></property>
</bean>
<!-- Spring Type Resolver for dynamic wiring -->
<bean id="workflowTypeResolver" class="com.opensymphony.workflow.util.SpringTypeResolver">
<!--
Here you can inject custom resolver for business logic
<property name="conditions">
<map>
<entry key="beanshell">
<value>mypackage.MyBeanShellCustomCondition</value></entry>
</map>
</property>
-->
</bean>
</beans>
Of special interest is the propertySetDelegate wiring with the Hibernate sessionFactory and that the bean is the DefaultHibernatePropertySetDelegate that is outlined above.
HibernatePropertySetDAOImpl wiring (application-context-hibernate.xml)
Recall the HibernatePropertySetDAOImpl class (above) uses the getHibernateTemplate() method via inheritance. In order for this to work the Hibernate sessionFactory must be wired into the class; specifically:
<bean id="hibernatePropertySetDAO" class="com.tricision.maas.dao.hibernate.HibernatePropertySetDAOImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
Finally...
This should be all you need to get Spring working with Hibernate 3 along with OSWorkflow and the PropertySet. Good luck!
|
|