/*
* Copyright 2002-2005 the original author or authors.
*
* 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.springmodules.workflow.osworkflow.configuration;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.FatalBeanException;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.util.Assert;
import org.xml.sax.SAXException;
import com.opensymphony.workflow.FactoryException;
import com.opensymphony.workflow.InvalidWorkflowDescriptorException;
import com.opensymphony.workflow.StoreException;
import com.opensymphony.workflow.config.DefaultConfiguration;
import com.opensymphony.workflow.loader.WorkflowDescriptor;
import com.opensymphony.workflow.loader.WorkflowLoader;
import com.opensymphony.workflow.spi.WorkflowStore;
import com.opensymphony.workflow.spi.memory.MemoryWorkflowStore;
/**
* Supports Spring-style configuration of OSWorkflow resources. <p/> Workflow
* descriptor resources are configured through the
* <code>workflowLocations</code> property. This property accepts a
* <code>Properties</code> instance and treats the key of each entry as the
* workflow name and the value as the resource path. All standard Spring
* resource paths are supported including <code>classpath:</code> style
* resources. <p/> By default <code>MemoryWorkflowStore</code> is used as the
* persistence class. However it is possible to use an already configured
* <code>WorkflowStore</code> by calling #setWorkflowStore. <p/>
*
* @author Rob Harrop
*/
public class ConfigurationBean extends DefaultConfiguration implements ResourceLoaderAware {
/**
* <code>Log</code> instance for this class.
*/
private static final Log logger = LogFactory.getLog(ConfigurationBean.class);
/**
* Spring <code>ResourceLoader</code> used to load workflow
* <code>Resource</code>s.
*
*/
protected ResourceLoader resourceLoader = new DefaultResourceLoader();
/**
* Stores any arguments that are to be passed to
*/
private Map persistenceArgs = new HashMap();
/**
* Stores loaded workflows
*/
private Map workflows = new HashMap();
/**
* Indicates whether this instance is initialized or not
*/
private boolean initialized;
/**
* User defined store - can be null.
*/
private WorkflowStore workflowStore;
/**
* Creates a new <code>ConfigurationBean</code> with
* <code>MemoryWorkflowStore</code> as the persistence class.
*/
public ConfigurationBean() {
setPersistence(MemoryWorkflowStore.class.getName());
}
/**
* Gets the <code>Map</code> of arguments to be passed to the persistence
* object
*/
public Map getPersistenceArgs() {
return this.persistenceArgs;
}
/**
* Sets the arguments to be passed to the persistence object.
*/
public void setPersistenceArgs(Map persistenceArgs) {
Assert.notEmpty(persistenceArgs, "persistenceArgs cannot be null or empty");
this.persistenceArgs = persistenceArgs;
}
/**
* Sets the locations of the workflow definition files as a
* <code>Properties</code> instance. The key of each entry corresponds to
* the logical name for the workflow definition and the value of each entry
* is the location of the definition file. <p/> Locations are specified as
* Spring-style resource paths and classpath: resources are fully supported.
*/
public void setWorkflowLocations(Properties workflowLocations) {
Assert.notNull(workflowLocations, "workflowLocations cannot be null");
Enumeration workflowNames = workflowLocations.propertyNames();
while (workflowNames.hasMoreElements()) {
String name = (String) workflowNames.nextElement();
String resourceLocation = workflowLocations.getProperty(name);
if (logger.isInfoEnabled()) {
logger.info("Loading workflow [" + name + "] from [" + resourceLocation + "].");
}
workflows.put(name, loadWorkflowDescriptor(resourceLocation, name));
}
this.initialized = true;
}
/**
* Gets a <code>WorkflowDescriptor</code> by name.
*/
public WorkflowDescriptor getWorkflow(String name) throws FactoryException {
WorkflowDescriptor wd = (WorkflowDescriptor) this.workflows.get(name);
if (wd == null) {
throw new FactoryException("Unknown workflow name [" + name + "].");
}
return wd;
}
/**
* Gets the names of all configured workflows.
*/
public String[] getWorkflowNames() throws FactoryException {
Set names = this.workflows.keySet();
return (String[]) names.toArray(new String[names.size()]);
}
/**
* Indicates whether this instance has been initialized or not.
*/
public boolean isInitialized() {
return this.initialized;
}
/**
* Unsupported operation.
*/
public boolean removeWorkflow(String string) throws FactoryException {
throw new UnsupportedOperationException("Operation removeWorkflow(String) not supported.");
}
/**
* Loads a <code>WorkflowDescriptor</code> from the specified location
* using the <code>WorkflowLoader</code> class.
*/
protected WorkflowDescriptor loadWorkflowDescriptor(String resourceLocation, String name) {
Resource resource = this.resourceLoader.getResource(resourceLocation);
WorkflowDescriptor workflowDescriptor = null;
try {
workflowDescriptor = this.invokeLoader(resource);
}
catch (IOException ex) {
throw new FatalBeanException("Unable to load workflow resource [" + resourceLocation + "].", ex);
}
catch (InvalidWorkflowDescriptorException ex) {
throw new FatalBeanException("Descriptor for workflow ["
+ name
+ "] in ["
+ resourceLocation
+ "] is invalid.", ex);
}
catch (SAXException ex) {
throw new FatalBeanException("XML in descriptorfor workflow ["
+ name
+ "] in ["
+ resourceLocation
+ "] is invalid.", ex);
}
return workflowDescriptor;
}
/**
* Hook which allows subclasses to invoke the approapriate/available method
* on the WorkflowLoader since between 2.7 and 2.8 the method signatures
* changed.
*
* @see com.opensymphony.workflow.loader.WorkflowLoader#load(java.io.InputStream)
*
* @param resource resource to load
* @throws WorkflowLoader.load exceptions
* @return loaded workflow descriptor
*/
protected WorkflowDescriptor invokeLoader(Resource resource) throws IOException, SAXException, InvalidWorkflowDescriptorException {
// oswf 2.8 version
// return WorkflowLoader.load(resource.getURL(), true);
// the method exists in both 2.7 and 2.8(deprecated)
return WorkflowLoader.load(resource.getInputStream());
}
/**
* @see com.opensymphony.workflow.config.DefaultConfiguration#getWorkflowStore()
*/
public WorkflowStore getWorkflowStore() throws StoreException {
// default behavior
if (workflowStore == null)
return super.getWorkflowStore();
return workflowStore;
}
/**
* @param workflowStore The workflowStore to set.
*/
public void setWorkflowStore(WorkflowStore workflowStore) {
this.workflowStore = workflowStore;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}