RESOLVED - issue SWF-363: Make the Flow Definition Registry Implementation Namespace Aware
http://opensource.atlassian.com/projects/spring/browse/SWF-363 Incomplete - task 2: Explore FlowExecutorArgumentHandler
This commit is contained in:
@@ -30,6 +30,10 @@ Package org.springframework.webflow.core
|
||||
* Fixed DefaultExpressionParserFactory to not require OGNL on the classpath if another expression parser
|
||||
is configured (SWF-335).
|
||||
|
||||
Package org.springframework.webflow.definition.registry
|
||||
* Added namespace awareness to the flow registry allowing flows with the same id to be placed in different namespaces
|
||||
and be referenced by a 'flow path' (SWF-363).
|
||||
|
||||
Package org.springframework.webflow.engine
|
||||
* Added invoke(String, Action) method to AbstractFlowBuilder.
|
||||
* Added initBuilder() hook method to AbstractFlowBuilder.
|
||||
|
||||
@@ -18,8 +18,8 @@ package org.springframework.webflow.definition.registry;
|
||||
import org.springframework.webflow.definition.FlowDefinition;
|
||||
|
||||
/**
|
||||
* A holder holding a reference to a Flow definition. Provides a layer of
|
||||
* indirection, enabling things like "hot-reloadable" flow definitions.
|
||||
* A holder holding a reference to a Flow definition. Provides a layer of indirection, enabling things like
|
||||
* "hot-reloadable" flow definitions.
|
||||
*
|
||||
* @see FlowDefinitionRegistry#registerFlowDefinition(FlowDefinitionHolder)
|
||||
*
|
||||
@@ -28,27 +28,23 @@ import org.springframework.webflow.definition.FlowDefinition;
|
||||
public interface FlowDefinitionHolder {
|
||||
|
||||
/**
|
||||
* Returns the <code>id</code> of the flow definition held by this holder.
|
||||
* This is a <i>lightweight</i> method callers may call to obtain the id of
|
||||
* the flow without triggering full flow definition assembly (which may be
|
||||
* an expensive operation).
|
||||
* Returns the <code>id</code> of the flow definition held by this holder. This is a <i>lightweight</i> method
|
||||
* callers may call to obtain the id of the flow without triggering full flow definition assembly (which may be an
|
||||
* expensive operation).
|
||||
*/
|
||||
public String getFlowDefinitionId();
|
||||
|
||||
/**
|
||||
* Returns the flow definition held by this holder. Calling this method the
|
||||
* first time may trigger flow assembly (which may be expensive).
|
||||
* @throws FlowDefinitionConstructionException if there is a problem constructing
|
||||
* the target flow definition
|
||||
* Returns the flow definition held by this holder. Calling this method the first time may trigger flow assembly
|
||||
* (which may be expensive).
|
||||
* @throws FlowDefinitionConstructionException if there is a problem constructing the target flow definition
|
||||
*/
|
||||
public FlowDefinition getFlowDefinition() throws FlowDefinitionConstructionException;
|
||||
|
||||
/**
|
||||
* Refresh the flow definition held by this holder. Calling this method
|
||||
* typically triggers flow reassembly, which may include a refresh from an
|
||||
* externalized resource such as a file.
|
||||
* @throws FlowDefinitionConstructionException if there is a problem constructing
|
||||
* the target flow definition
|
||||
* Refresh the flow definition held by this holder. Calling this method typically triggers flow re-assembly, which
|
||||
* may include a refresh from an externalized resource such as a file.
|
||||
* @throws FlowDefinitionConstructionException if there is a problem constructing the target flow definition
|
||||
*/
|
||||
public void refresh() throws FlowDefinitionConstructionException;
|
||||
}
|
||||
@@ -18,11 +18,10 @@ package org.springframework.webflow.definition.registry;
|
||||
import org.springframework.webflow.definition.FlowDefinition;
|
||||
|
||||
/**
|
||||
* A runtime service locator interface for retrieving flow definitions by
|
||||
* <code>id</code>.
|
||||
* A runtime service locator interface for retrieving flow definitions by <code>id</code>.
|
||||
* <p>
|
||||
* Flow locators are needed by flow executors at runtime to retrieve
|
||||
* fully-configured flow definitions to support launching new flow executions.
|
||||
* Flow locators are needed by flow executors at runtime to retrieve fully-configured flow definitions to support
|
||||
* launching new flow executions.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
@@ -30,14 +29,12 @@ import org.springframework.webflow.definition.FlowDefinition;
|
||||
public interface FlowDefinitionLocator {
|
||||
|
||||
/**
|
||||
* Lookup the flow definition with the specified <code>id</code>.
|
||||
* @param id the flow definition id
|
||||
* Lookup the flow definition with the specified <code>path</code>.
|
||||
* @param flowPath the flow definition path
|
||||
* @return the flow definition
|
||||
* @throws NoSuchFlowDefinitionException when the flow definition with the
|
||||
* specified id does not exist
|
||||
* @throws FlowDefinitionConstructionException if there is a problem constructing
|
||||
* the identified flow definition
|
||||
* @throws NoSuchFlowDefinitionException when the flow definition with the specified id does not exist
|
||||
* @throws FlowDefinitionConstructionException if there is a problem constructing the identified flow definition
|
||||
*/
|
||||
public FlowDefinition getFlowDefinition(String id)
|
||||
throws NoSuchFlowDefinitionException, FlowDefinitionConstructionException;
|
||||
public FlowDefinition getFlowDefinition(String flowPath) throws NoSuchFlowDefinitionException,
|
||||
FlowDefinitionConstructionException;
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.webflow.definition.registry;
|
||||
|
||||
import org.springframework.webflow.definition.FlowDefinition;
|
||||
|
||||
/**
|
||||
* A container of flow definitions. Extends the {@link FlowDefinitionRegistryMBean} management interface exposing
|
||||
@@ -26,6 +25,7 @@ import org.springframework.webflow.definition.FlowDefinition;
|
||||
* registry hierarchy.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Ben Hale
|
||||
*/
|
||||
public interface FlowDefinitionRegistry extends FlowDefinitionLocator, FlowDefinitionRegistryMBean {
|
||||
|
||||
@@ -36,21 +36,21 @@ public interface FlowDefinitionRegistry extends FlowDefinitionLocator, FlowDefin
|
||||
*/
|
||||
public void setParent(FlowDefinitionRegistry parent);
|
||||
|
||||
/**
|
||||
* Return all flow definitions registered in this registry. Note that this will trigger flow assembly for all
|
||||
* registered flow definitions (which may be expensive).
|
||||
* @return the flow definitions
|
||||
* @throws FlowDefinitionConstructionException if there is a problem constructing one of the registered flow
|
||||
* definitions
|
||||
*/
|
||||
public FlowDefinition[] getFlowDefinitions() throws FlowDefinitionConstructionException;
|
||||
|
||||
/**
|
||||
* Register a flow definition in this registry. Registers a "holder", not the Flow definition itself. This allows
|
||||
* the actual Flow definition to be loaded lazily only when needed, and also rebuilt at runtime when its underlying
|
||||
* resource changes without redeploy.
|
||||
* resource changes without re-deploy.
|
||||
* @param flowHolder a holder holding the flow definition to register
|
||||
*/
|
||||
public void registerFlowDefinition(FlowDefinitionHolder flowHolder);
|
||||
|
||||
/**
|
||||
* Register a flow definition in this registry under a specific namespace. Registers a "holder", not the Flow
|
||||
* definition itself. This allows the actual Flow definition to be loaded lazily only when needed, and also rebuilt
|
||||
* at runtime when its underlying resource changes without re-deploy.
|
||||
* @param flowHolder a holder holding the flow definition to register
|
||||
* @param namespace the namespace to register the flow definition in
|
||||
*/
|
||||
public void registerFlowDefinition(FlowDefinitionHolder flowHolder, String namespace);
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package org.springframework.webflow.definition.registry;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@@ -34,200 +35,192 @@ import org.springframework.webflow.definition.FlowDefinition;
|
||||
* definitions that no longer exist.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Ben Hale
|
||||
*/
|
||||
public class FlowDefinitionRegistryImpl implements FlowDefinitionRegistry {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(FlowDefinitionRegistryImpl.class);
|
||||
private static final Log logger = LogFactory.getLog(FlowDefinitionRegistryImpl.class);
|
||||
|
||||
/**
|
||||
* The map of loaded Flow definitions maintained in this registry.
|
||||
*/
|
||||
private Map flowDefinitions;
|
||||
/**
|
||||
* The map of loaded Flow definitions maintained in this registry.
|
||||
*/
|
||||
private Map flowDefinitions;
|
||||
|
||||
/**
|
||||
* An optional parent flow definition registry.
|
||||
*/
|
||||
private FlowDefinitionRegistry parent;
|
||||
/**
|
||||
* An optional parent flow definition registry.
|
||||
*/
|
||||
private FlowDefinitionRegistry parent;
|
||||
|
||||
public FlowDefinitionRegistryImpl() {
|
||||
flowDefinitions = new TreeMap();
|
||||
}
|
||||
|
||||
// implementing FlowDefinitionRegistryMBean
|
||||
|
||||
public String[] getFlowDefinitionIds() {
|
||||
return (String[]) flowDefinitions.keySet().toArray(new String[flowDefinitions.size()]);
|
||||
}
|
||||
|
||||
public int getFlowDefinitionCount() {
|
||||
return flowDefinitions.size();
|
||||
}
|
||||
|
||||
public boolean containsFlowDefinition(String id) {
|
||||
Assert.hasText(id, "The flow id is required");
|
||||
return flowDefinitions.get(id) != null;
|
||||
}
|
||||
|
||||
public void refresh() throws FlowDefinitionConstructionException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Refreshing flow definition registry '" + this + "'");
|
||||
public FlowDefinitionRegistryImpl() {
|
||||
flowDefinitions = new TreeMap();
|
||||
}
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
// workaround for JMX
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
LinkedList needsReindexing = new LinkedList();
|
||||
Iterator it = flowDefinitions.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
String key = (String) entry.getKey();
|
||||
FlowDefinitionHolder holder = (FlowDefinitionHolder) entry.getValue();
|
||||
holder.refresh();
|
||||
if (!holder.getFlowDefinitionId().equals(key)) {
|
||||
needsReindexing.add(new Indexed(key, holder));
|
||||
|
||||
// implementing FlowDefinitionRegistryMBean
|
||||
|
||||
public String[] getFlowDefinitionPaths() {
|
||||
List flowPaths = new LinkedList();
|
||||
for (Iterator namespaces = flowDefinitions.entrySet().iterator(); namespaces.hasNext();) {
|
||||
Map.Entry namespaceEntry = (Map.Entry) namespaces.next();
|
||||
String namespaceName = (String) namespaceEntry.getKey();
|
||||
Map namespace = (Map) namespaceEntry.getValue();
|
||||
for (Iterator ids = namespace.keySet().iterator(); ids.hasNext();) {
|
||||
flowPaths.add(FlowPathUtils.buildFlowPath(namespaceName, (String) ids.next()));
|
||||
}
|
||||
}
|
||||
}
|
||||
it = needsReindexing.iterator();
|
||||
while (it.hasNext()) {
|
||||
Indexed indexed = (Indexed) it.next();
|
||||
reindex(indexed.holder, indexed.key);
|
||||
}
|
||||
} finally {
|
||||
Thread.currentThread().setContextClassLoader(loader);
|
||||
return (String[]) flowPaths.toArray(new String[flowPaths.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
public void refresh(String flowId) throws NoSuchFlowDefinitionException, FlowDefinitionConstructionException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Refreshing flow with id '" + flowId + "'");
|
||||
public int getFlowDefinitionCount() {
|
||||
int count = 0;
|
||||
for (Iterator namespaces = flowDefinitions.values().iterator(); namespaces.hasNext();) {
|
||||
Map namespace = (Map) namespaces.next();
|
||||
count += namespace.size();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
// workaround for JMX
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
FlowDefinitionHolder holder = getFlowDefinitionHolder(flowId);
|
||||
holder.refresh();
|
||||
if (!holder.getFlowDefinitionId().equals(flowId)) {
|
||||
reindex(holder, flowId);
|
||||
}
|
||||
} finally {
|
||||
Thread.currentThread().setContextClassLoader(loader);
|
||||
|
||||
public boolean containsFlowDefinition(String flowPath) {
|
||||
Assert.hasText(flowPath, "The flow path is required");
|
||||
Map namespace = getNamespace(FlowPathUtils.extractFlowNamespace(flowPath));
|
||||
return namespace.containsKey(FlowPathUtils.extractFlowId(flowPath));
|
||||
}
|
||||
}
|
||||
|
||||
// implementing FlowDefinitionLocator
|
||||
|
||||
public FlowDefinition getFlowDefinition(String id) throws NoSuchFlowDefinitionException,
|
||||
FlowDefinitionConstructionException {
|
||||
Assert.hasText(id,
|
||||
"Unable to load a flow definition: no flow id was provided. Please provide a valid flow identifier.");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Getting flow definition with id '" + id + "'");
|
||||
public void refresh() throws FlowDefinitionConstructionException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Refreshing flow definition registry '" + this + "'");
|
||||
}
|
||||
for (Iterator namespaces = flowDefinitions.entrySet().iterator(); namespaces.hasNext();) {
|
||||
Map.Entry namespaceEntry = (Map.Entry) namespaces.next();
|
||||
String namespaceName = (String) namespaceEntry.getKey();
|
||||
Map namespace = (Map) namespaceEntry.getValue();
|
||||
for (Iterator ids = namespace.keySet().iterator(); ids.hasNext();) {
|
||||
refresh(FlowPathUtils.buildFlowPath(namespaceName, (String) ids.next()));
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
return getFlowDefinitionHolder(id).getFlowDefinition();
|
||||
} catch (NoSuchFlowDefinitionException e) {
|
||||
if (parent != null) {
|
||||
// try parent
|
||||
return parent.getFlowDefinition(id);
|
||||
}
|
||||
throw e;
|
||||
|
||||
public void refresh(String flowPath) throws NoSuchFlowDefinitionException, FlowDefinitionConstructionException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Refreshing flow with path '" + flowPath + "'");
|
||||
}
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
// workaround for JMX
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
FlowDefinitionHolder holder = getFlowDefinitionHolder(flowPath);
|
||||
holder.refresh();
|
||||
if (!holder.getFlowDefinitionId().equals(FlowPathUtils.extractFlowId(flowPath))) {
|
||||
reindex(holder, FlowPathUtils.extractFlowNamespace(flowPath), flowPath);
|
||||
}
|
||||
} finally {
|
||||
Thread.currentThread().setContextClassLoader(loader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// implementing FlowDefinitionRegistry
|
||||
// implementing FlowDefinitionLocator
|
||||
|
||||
public void setParent(FlowDefinitionRegistry parent) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Setting parent flow definition registry to '" + parent + "'");
|
||||
public FlowDefinition getFlowDefinition(String path) throws NoSuchFlowDefinitionException,
|
||||
FlowDefinitionConstructionException {
|
||||
Assert.hasText(path,
|
||||
"Unable to load a flow definition: no flow path was provided. Please provide a valid flow path.");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Getting flow definition with path '" + path + "'");
|
||||
}
|
||||
try {
|
||||
return getFlowDefinitionHolder(path).getFlowDefinition();
|
||||
} catch (NoSuchFlowDefinitionException e) {
|
||||
if (parent != null) {
|
||||
// try parent
|
||||
return parent.getFlowDefinition(path);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public FlowDefinition[] getFlowDefinitions() throws FlowDefinitionConstructionException {
|
||||
FlowDefinition[] flows = new FlowDefinition[flowDefinitions.size()];
|
||||
Iterator it = flowDefinitions.values().iterator();
|
||||
int i = 0;
|
||||
while (it.hasNext()) {
|
||||
FlowDefinitionHolder holder = (FlowDefinitionHolder) it.next();
|
||||
flows[i] = holder.getFlowDefinition();
|
||||
i++;
|
||||
// implementing FlowDefinitionRegistry
|
||||
|
||||
public void setParent(FlowDefinitionRegistry parent) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Setting parent flow definition registry to '" + parent + "'");
|
||||
}
|
||||
this.parent = parent;
|
||||
}
|
||||
return flows;
|
||||
}
|
||||
|
||||
public void registerFlowDefinition(FlowDefinitionHolder flowHolder) {
|
||||
Assert.notNull(flowHolder, "The flow definition holder to register is required");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Registering flow definition with id '" + flowHolder.getFlowDefinitionId() + "'");
|
||||
public void registerFlowDefinition(FlowDefinitionHolder flowHolder) {
|
||||
registerFlowDefinition(flowHolder, "");
|
||||
}
|
||||
index(flowHolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove identified flow definition from this registry. If the given id is not known in this registry, nothing will
|
||||
* happen.
|
||||
* @param id the flow definition id
|
||||
*/
|
||||
public void removeFlowDefinition(String id) {
|
||||
Assert.hasText(id, "The flow id is required");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Removing flow definition with id '" + id + "'");
|
||||
public void registerFlowDefinition(FlowDefinitionHolder flowHolder, String namespace) {
|
||||
Assert.notNull(flowHolder, "The flow definition holder to register is required");
|
||||
Assert.notNull(namespace, "The flow namespace is required");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Registering flow definition with id '" + flowHolder.getFlowDefinitionId()
|
||||
+ "' in namespace '" + namespace + "'");
|
||||
}
|
||||
index(flowHolder, namespace);
|
||||
}
|
||||
flowDefinitions.remove(id);
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
/**
|
||||
* Reindex given flow definition.
|
||||
* @param holder the holder holding the flow definition to reindex
|
||||
* @param oldId the id that was previously assigned to given flow definition
|
||||
*/
|
||||
private void reindex(FlowDefinitionHolder holder, String oldId) {
|
||||
flowDefinitions.remove(oldId);
|
||||
index(holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Index given flow definition.
|
||||
* @param holder the holder holding the flow definition to index
|
||||
*/
|
||||
private void index(FlowDefinitionHolder holder) {
|
||||
Assert.hasText(holder.getFlowDefinitionId(), "The flow holder to index must return a non-blank flow id");
|
||||
flowDefinitions.put(holder.getFlowDefinitionId(), holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identified flow definition holder. Throws an exception if it cannot be found.
|
||||
*/
|
||||
private FlowDefinitionHolder getFlowDefinitionHolder(String id) throws NoSuchFlowDefinitionException {
|
||||
FlowDefinitionHolder flowHolder = (FlowDefinitionHolder) flowDefinitions.get(id);
|
||||
if (flowHolder == null) {
|
||||
throw new NoSuchFlowDefinitionException(id, getFlowDefinitionIds());
|
||||
/**
|
||||
* Remove identified flow definition from this registry. If the given id is not known in this registry, nothing will
|
||||
* happen.
|
||||
* @param flowPath the flow definition path
|
||||
*/
|
||||
public void removeFlowDefinition(String flowPath) {
|
||||
Assert.hasText(flowPath, "The flow path is required");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Removing flow definition with path '" + flowPath + "'");
|
||||
}
|
||||
Map namespace = getNamespace(FlowPathUtils.extractFlowNamespace(flowPath));
|
||||
namespace.remove(FlowPathUtils.extractFlowId(flowPath));
|
||||
}
|
||||
return flowHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple value object that holds the key for an indexed flow definition holder in this registry. Used to support
|
||||
* reindexing on a refresh.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
private static class Indexed {
|
||||
// internal helpers
|
||||
|
||||
private String key;
|
||||
|
||||
private FlowDefinitionHolder holder;
|
||||
|
||||
public Indexed(String key, FlowDefinitionHolder holder) {
|
||||
this.key = key;
|
||||
this.holder = holder;
|
||||
/**
|
||||
* Re-index given flow definition.
|
||||
* @param holder the holder holding the flow definition to re-index
|
||||
* @param namespace the namespace to index the new flow in
|
||||
* @param oldFlowPath the flowPath that was previously assigned to given flow definition
|
||||
*/
|
||||
private void reindex(FlowDefinitionHolder holder, String namespace, String oldFlowPath) {
|
||||
removeFlowDefinition(oldFlowPath);
|
||||
index(holder, namespace);
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("flowDefinitions", flowDefinitions).append("parent", parent).toString();
|
||||
}
|
||||
/**
|
||||
* Index given flow definition.
|
||||
* @param holder the holder holding the flow definition to index
|
||||
* @param namespaceName the namespace to index the flow definition in
|
||||
*/
|
||||
private void index(FlowDefinitionHolder holder, String namespaceName) {
|
||||
Assert.hasText(holder.getFlowDefinitionId(), "The flow holder to index must return a non-blank flow id");
|
||||
Map namespace = getNamespace(namespaceName);
|
||||
namespace.put(holder.getFlowDefinitionId(), holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identified flow definition holder. Throws an exception if it cannot be found.
|
||||
*/
|
||||
private FlowDefinitionHolder getFlowDefinitionHolder(String flowPath) throws NoSuchFlowDefinitionException {
|
||||
Map namespace = getNamespace(FlowPathUtils.extractFlowNamespace(flowPath));
|
||||
FlowDefinitionHolder flowHolder = (FlowDefinitionHolder) namespace.get(FlowPathUtils.extractFlowId(flowPath));
|
||||
if (flowHolder == null) {
|
||||
throw new NoSuchFlowDefinitionException(flowPath, getFlowDefinitionPaths());
|
||||
}
|
||||
return flowHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace map for a given namespace. Creates the map if it does not exist.
|
||||
*/
|
||||
private Map getNamespace(String namespace) {
|
||||
if (!flowDefinitions.containsKey(namespace)) {
|
||||
flowDefinitions.put(namespace, new TreeMap());
|
||||
}
|
||||
return (Map) flowDefinitions.get(namespace);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("flowDefinitions", flowDefinitions).append("parent", parent).toString();
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,12 @@
|
||||
package org.springframework.webflow.definition.registry;
|
||||
|
||||
/**
|
||||
* A management interface for managing flow definition registries at runtime.
|
||||
* Provides the ability to query the size and state of the registry, as well as
|
||||
* refresh registered flow definitions at runtime.
|
||||
* A management interface for managing flow definition registries at runtime. Provides the ability to query the size and
|
||||
* state of the registry, as well as refresh registered flow definitions at runtime.
|
||||
* <p>
|
||||
* Flow registries that implement this interface may be exposed for management
|
||||
* over the JMX protocol. The following is an example of using Spring's JMX
|
||||
* <code>MBeanExporter</code> to export a flow registry to an MBeanServer:
|
||||
* Flow registries that implement this interface may be exposed for management over the JMX protocol. The following is
|
||||
* an example of using Spring's JMX <code>MBeanExporter</code> to export a flow registry to an MBeanServer:
|
||||
*
|
||||
* <pre class="code">
|
||||
* <!-- Creates the registry of flow definitions for this application -->
|
||||
* <bean name="flowRegistry" class="org.springframework.webflow...XmlFlowRegistryFactoryBean">
|
||||
@@ -44,18 +43,19 @@ package org.springframework.webflow.definition.registry;
|
||||
* </property>
|
||||
* </bean>
|
||||
* </pre>
|
||||
* With the above configuration, you may then use any JMX client (such as Sun's
|
||||
* jConsole which ships with JDK 1.5) to refresh flow definitions at runtime.
|
||||
*
|
||||
* With the above configuration, you may then use any JMX client (such as Sun's jConsole which ships with JDK 1.5) to
|
||||
* refresh flow definitions at runtime.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public interface FlowDefinitionRegistryMBean {
|
||||
|
||||
/**
|
||||
* Returns the ids of the flow definitions registered in this registry.
|
||||
* @return the flow definition ids
|
||||
* Returns the paths of the flow definitions registered in this registry.
|
||||
* @return the flow definition paths
|
||||
*/
|
||||
public String[] getFlowDefinitionIds();
|
||||
public String[] getFlowDefinitionPaths();
|
||||
|
||||
/**
|
||||
* Return the number of flow definitions registered in this registry.
|
||||
@@ -64,28 +64,23 @@ public interface FlowDefinitionRegistryMBean {
|
||||
public int getFlowDefinitionCount();
|
||||
|
||||
/**
|
||||
* Queries this registry to determine if a specific flow is contained within
|
||||
* it.
|
||||
* @param id the flow definition id
|
||||
* @return true if a flow definition is contained in this registry with the
|
||||
* id provided
|
||||
* Queries this registry to determine if a specific flow is contained within it.
|
||||
* @param flowPath the flow definition path
|
||||
* @return true if a flow definition is contained in this registry with the id provided
|
||||
*/
|
||||
public boolean containsFlowDefinition(String id);
|
||||
public boolean containsFlowDefinition(String flowPath);
|
||||
|
||||
/**
|
||||
* Refresh this flow definition registry, reloading all Flow definitions
|
||||
* from their externalized representations.
|
||||
* Refresh this flow definition registry, reloading all Flow definitions from their externalized representations.
|
||||
*/
|
||||
public void refresh() throws FlowDefinitionConstructionException;
|
||||
|
||||
/**
|
||||
* Refresh the Flow definition in this registry with the <code>id</code>
|
||||
* provided, reloading it from it's externalized representation.
|
||||
* @param flowDefinitionId the id of the flow definition to refresh
|
||||
* @throws NoSuchFlowDefinitionException if a flow with the id provided is not
|
||||
* stored in this registry
|
||||
* Refresh the Flow definition in this registry with the <code>path</code> provided, reloading it from it's
|
||||
* externalized representation.
|
||||
* @param flowPath the path of the flow definition to refresh
|
||||
* @throws NoSuchFlowDefinitionException if a flow with the id provided is not stored in this registry
|
||||
*/
|
||||
public void refresh(String flowDefinitionId)
|
||||
throws NoSuchFlowDefinitionException, FlowDefinitionConstructionException;
|
||||
public void refresh(String flowPath) throws NoSuchFlowDefinitionException, FlowDefinitionConstructionException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.springframework.webflow.definition.registry;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple utility for working with flow paths. Only intended for internal use.
|
||||
*
|
||||
* @author Ben Hale
|
||||
*/
|
||||
class FlowPathUtils {
|
||||
|
||||
private static final String PATH_DELIMITER = "/";
|
||||
|
||||
/**
|
||||
* Parses a flow path and returns the namespace part of the path.
|
||||
* @param flowPath The path to parse
|
||||
* @return The namespace part of the path
|
||||
*/
|
||||
public static String extractFlowNamespace(String flowPath) {
|
||||
Assert.hasText(flowPath, "The flow path must not be empty or null");
|
||||
int index = flowPath.lastIndexOf(PATH_DELIMITER);
|
||||
if (index == -1) {
|
||||
return "";
|
||||
} else {
|
||||
return flowPath.substring(0, index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a flow path and returns the id part of the path.
|
||||
* @param flowPath The path to parse
|
||||
* @return The id part of the path
|
||||
*/
|
||||
public static String extractFlowId(String flowPath) {
|
||||
Assert.hasText(flowPath, "The flow path must not be empty or null");
|
||||
int index = flowPath.lastIndexOf(PATH_DELIMITER);
|
||||
if (index == -1) {
|
||||
return flowPath;
|
||||
} else {
|
||||
return flowPath.substring(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a flow path based on given namespace and id.
|
||||
* @param namespace The namespace of the path
|
||||
* @param id The id of the path
|
||||
* @return The complete flow path
|
||||
*/
|
||||
public static String buildFlowPath(String namespace, String id) {
|
||||
Assert.notNull(namespace, "namespace must have some value");
|
||||
Assert.hasText(id, "The id must not be empty or null");
|
||||
return namespace + PATH_DELIMITER + id;
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,7 @@ package org.springframework.webflow.definition.registry;
|
||||
import org.springframework.webflow.definition.FlowDefinition;
|
||||
|
||||
/**
|
||||
* A simple flow definition holder that just holds a constant singleton
|
||||
* reference to a flow definition.
|
||||
* A simple flow definition holder that just holds a constant singleton reference to a flow definition.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
|
||||
@@ -27,14 +27,13 @@ import org.springframework.webflow.engine.Flow;
|
||||
import org.springframework.webflow.util.ResourceHolder;
|
||||
|
||||
/**
|
||||
* A flow definition holder that can detect changes on an underlying flow
|
||||
* definition resource and refresh that resource automatically.
|
||||
* A flow definition holder that can detect changes on an underlying flow definition resource and refresh that resource
|
||||
* automatically.
|
||||
* <p>
|
||||
* This class is threadsafe.
|
||||
* This class is thread-safe.
|
||||
* <p>
|
||||
* Note that this {@link FlowDefinition} holder uses a {@link Flow} assembler.
|
||||
* This is normal since a {@link Flow} is a {@link FlowDefinition}! This class
|
||||
* bridges the <i>abstract</i> world of {@link FlowDefinition flow definitions}
|
||||
* Note that this {@link FlowDefinition} holder uses a {@link Flow} assembler. This is normal since a {@link Flow} is a
|
||||
* {@link FlowDefinition}! This class bridges the <i>abstract</i> world of {@link FlowDefinition flow definitions}
|
||||
* with the <i>concrete</i> world of {@link Flow flow implementations}.
|
||||
*
|
||||
* @see FlowDefinition
|
||||
@@ -44,7 +43,7 @@ import org.springframework.webflow.util.ResourceHolder;
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
|
||||
|
||||
private static final Log logger = LogFactory.getLog(RefreshableFlowDefinitionHolder.class);
|
||||
|
||||
/**
|
||||
@@ -58,21 +57,19 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
private FlowAssembler assembler;
|
||||
|
||||
/**
|
||||
* A last modified date for the backing flow definition resource, used to support
|
||||
* automatic reassembly on resource change.
|
||||
* A last modified date for the backing flow definition resource, used to support automatic reassembly on resource
|
||||
* change.
|
||||
*/
|
||||
private long lastModified;
|
||||
|
||||
/**
|
||||
* A flag indicating whether or not this holder is in the middle of the
|
||||
* assembly process.
|
||||
* A flag indicating whether or not this holder is in the middle of the assembly process.
|
||||
*/
|
||||
private boolean assembling;
|
||||
|
||||
/**
|
||||
* Creates a new refreshable flow definition holder that uses the configured
|
||||
* assembler (GOF director) to drive flow assembly, on initial use and on any
|
||||
* resource change or refresh.
|
||||
* Creates a new refreshable flow definition holder that uses the configured assembler (GOF director) to drive flow
|
||||
* assembly, on initial use and on any resource change or refresh.
|
||||
* @param assembler the flow assembler to use
|
||||
*/
|
||||
public RefreshableFlowDefinitionHolder(FlowAssembler assembler) {
|
||||
@@ -91,8 +88,7 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
if (!isAssembled()) {
|
||||
lastModified = calculateLastModified();
|
||||
assembleFlow();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
refreshIfChanged();
|
||||
}
|
||||
return flowDefinition;
|
||||
@@ -101,7 +97,7 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
public synchronized void refresh() throws FlowBuilderException {
|
||||
assembleFlow();
|
||||
}
|
||||
|
||||
|
||||
// internal helpers
|
||||
|
||||
/**
|
||||
@@ -122,8 +118,8 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
long calculatedLastModified = calculateLastModified();
|
||||
if (this.lastModified < calculatedLastModified) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Resource modification detected, reloading flow definition with id '" +
|
||||
assembler.getFlowId() + "'");
|
||||
logger.debug("Resource modification detected, reloading flow definition with id '"
|
||||
+ assembler.getFlowId() + "'");
|
||||
}
|
||||
assembleFlow();
|
||||
this.lastModified = calculatedLastModified;
|
||||
@@ -131,21 +127,18 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper that retrieves the last modified date by querying the backing flow
|
||||
* resource.
|
||||
* Helper that retrieves the last modified date by querying the backing flow resource.
|
||||
* @return the last modified date, or -1 if it could not be retrieved
|
||||
*/
|
||||
protected long calculateLastModified() {
|
||||
if (getFlowBuilder() instanceof ResourceHolder) {
|
||||
Resource resource = ((ResourceHolder)getFlowBuilder()).getResource();
|
||||
Resource resource = ((ResourceHolder) getFlowBuilder()).getResource();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
"Calculating last modified timestamp for flow definition resource '" + resource + "'");
|
||||
logger.debug("Calculating last modified timestamp for flow definition resource '" + resource + "'");
|
||||
}
|
||||
try {
|
||||
return resource.getFile().lastModified();
|
||||
}
|
||||
catch (IOException e) {
|
||||
} catch (IOException e) {
|
||||
// ignore, last modified checks not supported
|
||||
}
|
||||
}
|
||||
@@ -153,7 +146,7 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modifed date of the backed flow definition resource.
|
||||
* Returns the last modified date of the backed flow definition resource.
|
||||
* @return the last modified date
|
||||
*/
|
||||
protected long getLastModified() {
|
||||
@@ -161,8 +154,7 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble the held flow definition, delegating to the configured
|
||||
* FlowAssembler (director).
|
||||
* Assemble the held flow definition, delegating to the configured FlowAssembler (director).
|
||||
*/
|
||||
protected void assembleFlow() throws FlowBuilderException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -171,15 +163,13 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
try {
|
||||
assembling = true;
|
||||
flowDefinition = assembler.assembleFlow();
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
assembling = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flag indicating if this holder has performed and completed
|
||||
* flow definition assembly.
|
||||
* Returns a flag indicating if this holder has performed and completed flow definition assembly.
|
||||
*/
|
||||
protected boolean isAssembled() {
|
||||
return flowDefinition != null;
|
||||
|
||||
@@ -25,27 +25,36 @@ import org.springframework.webflow.definition.StateDefinition;
|
||||
* Unit tests for {@link FlowDefinitionRegistryImpl}.
|
||||
*/
|
||||
public class FlowDefinitionRegistryImplTests extends TestCase {
|
||||
|
||||
|
||||
private FlowDefinitionRegistryImpl registry = new FlowDefinitionRegistryImpl();
|
||||
|
||||
private FlowDefinition fooFlow;
|
||||
|
||||
private FlowDefinition barFlow;
|
||||
|
||||
protected void setUp() {
|
||||
fooFlow = new FooFlow();
|
||||
barFlow = new BarFlow();
|
||||
}
|
||||
|
||||
public void testEmptyRegistryAsserts() {
|
||||
assertEquals(0, registry.getFlowDefinitionCount());
|
||||
assertEquals(0, registry.getFlowDefinitionIds().length);
|
||||
assertEquals(0, registry.getFlowDefinitions().length);
|
||||
assertEquals(0, registry.getFlowDefinitionPaths().length);
|
||||
}
|
||||
|
||||
public void testNoSuchFlowDefinition() {
|
||||
try {
|
||||
registry.getFlowDefinition("bogus");
|
||||
fail("Should've bombed with NoSuchFlow");
|
||||
} catch (NoSuchFlowDefinitionException e) {
|
||||
}
|
||||
catch (NoSuchFlowDefinitionException e) {
|
||||
}
|
||||
|
||||
public void testNoSuchFlowDefinitionWithNamespace() {
|
||||
try {
|
||||
registry.getFlowDefinition("/namespace/bogus");
|
||||
fail("Should've bombed with NoSuchFlow");
|
||||
} catch (NoSuchFlowDefinitionException e) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -53,11 +62,17 @@ public class FlowDefinitionRegistryImplTests extends TestCase {
|
||||
public void testRegisterFlow() {
|
||||
registry.registerFlowDefinition(new StaticFlowDefinitionHolder(fooFlow));
|
||||
assertEquals(1, registry.getFlowDefinitionCount());
|
||||
assertEquals("foo", registry.getFlowDefinitionIds()[0]);
|
||||
assertEquals("foo", registry.getFlowDefinitions()[0].getId());
|
||||
assertEquals("/foo", registry.getFlowDefinitionPaths()[0]);
|
||||
assertEquals("foo", registry.getFlowDefinition("foo").getId());
|
||||
}
|
||||
|
||||
public void testRegisterFlowWithNamespace() {
|
||||
registry.registerFlowDefinition(new StaticFlowDefinitionHolder(barFlow), "/namespace");
|
||||
assertEquals(1, registry.getFlowDefinitionCount());
|
||||
assertEquals("/namespace/bar", registry.getFlowDefinitionPaths()[0]);
|
||||
assertEquals("bar", registry.getFlowDefinition("/namespace/bar").getId());
|
||||
}
|
||||
|
||||
public void testRegisterFlowSameIds() {
|
||||
registry.registerFlowDefinition(new StaticFlowDefinitionHolder(fooFlow));
|
||||
FooFlow newFlow = new FooFlow();
|
||||
@@ -66,6 +81,14 @@ public class FlowDefinitionRegistryImplTests extends TestCase {
|
||||
assertSame(newFlow, registry.getFlowDefinition("foo"));
|
||||
}
|
||||
|
||||
public void testRegisterFlowSameIdsWithNamespace() {
|
||||
registry.registerFlowDefinition(new StaticFlowDefinitionHolder(barFlow), "/namespace");
|
||||
BarFlow newFlow = new BarFlow();
|
||||
registry.registerFlowDefinition(new StaticFlowDefinitionHolder(newFlow), "/namespace");
|
||||
assertEquals(1, registry.getFlowDefinitionCount());
|
||||
assertSame(newFlow, registry.getFlowDefinition("/namespace/bar"));
|
||||
}
|
||||
|
||||
public void testRegisterMultipleFlows() {
|
||||
registry.registerFlowDefinition(new StaticFlowDefinitionHolder(fooFlow));
|
||||
FooFlow newFlow = new FooFlow();
|
||||
@@ -76,13 +99,30 @@ public class FlowDefinitionRegistryImplTests extends TestCase {
|
||||
assertSame(newFlow, registry.getFlowDefinition("bar"));
|
||||
}
|
||||
|
||||
public void testRegisterMultipleFlowsWithNamespace() {
|
||||
registry.registerFlowDefinition(new StaticFlowDefinitionHolder(barFlow), "/namespace");
|
||||
BarFlow newFlow = new BarFlow();
|
||||
newFlow.id = "foo";
|
||||
registry.registerFlowDefinition(new StaticFlowDefinitionHolder(newFlow), "/namespace");
|
||||
assertEquals(2, registry.getFlowDefinitionCount());
|
||||
assertSame(barFlow, registry.getFlowDefinition("/namespace/bar"));
|
||||
assertSame(newFlow, registry.getFlowDefinition("/namespace/foo"));
|
||||
}
|
||||
|
||||
public void testRefresh() {
|
||||
testRegisterMultipleFlows();
|
||||
registry.refresh();
|
||||
assertEquals(2, registry.getFlowDefinitionCount());
|
||||
assertSame(fooFlow, registry.getFlowDefinition("foo"));
|
||||
}
|
||||
|
||||
|
||||
public void testRefreshWithNamespace() {
|
||||
testRegisterMultipleFlowsWithNamespace();
|
||||
registry.refresh();
|
||||
assertEquals(2, registry.getFlowDefinitionCount());
|
||||
assertSame(barFlow, registry.getFlowDefinition("/namespace/bar"));
|
||||
}
|
||||
|
||||
public void testRefreshValidFlow() {
|
||||
testRegisterMultipleFlows();
|
||||
registry.refresh("foo");
|
||||
@@ -90,17 +130,33 @@ public class FlowDefinitionRegistryImplTests extends TestCase {
|
||||
assertSame(fooFlow, registry.getFlowDefinition("foo"));
|
||||
}
|
||||
|
||||
public void testRefreshValidFlowWithNamespace() {
|
||||
testRegisterMultipleFlowsWithNamespace();
|
||||
registry.refresh("/namespace/bar");
|
||||
assertEquals(2, registry.getFlowDefinitionCount());
|
||||
assertSame(barFlow, registry.getFlowDefinition("/namespace/bar"));
|
||||
}
|
||||
|
||||
public void testRefreshNoSuchFlow() {
|
||||
testRegisterMultipleFlows();
|
||||
try {
|
||||
registry.refresh("bogus");
|
||||
fail("Should've bombed with NoSuchFlow");
|
||||
}
|
||||
catch (NoSuchFlowDefinitionException e) {
|
||||
} catch (NoSuchFlowDefinitionException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testRefreshNoSuchFlowWithNamespace() {
|
||||
testRegisterMultipleFlowsWithNamespace();
|
||||
try {
|
||||
registry.refresh("/namespace/bogus");
|
||||
fail("Should've bombed with NoSuchFlow");
|
||||
} catch (NoSuchFlowDefinitionException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void testParentHierarchy() {
|
||||
testRegisterMultipleFlows();
|
||||
FlowDefinitionRegistryImpl child = new FlowDefinitionRegistryImpl();
|
||||
@@ -111,9 +167,47 @@ public class FlowDefinitionRegistryImplTests extends TestCase {
|
||||
assertEquals("bar", child.getFlowDefinition("bar").getId());
|
||||
}
|
||||
|
||||
public void testParentHierarchyWithNamespace() {
|
||||
testRegisterMultipleFlowsWithNamespace();
|
||||
FlowDefinitionRegistryImpl child = new FlowDefinitionRegistryImpl();
|
||||
child.setParent(registry);
|
||||
BarFlow barFlow = new BarFlow();
|
||||
child.registerFlowDefinition(new StaticFlowDefinitionHolder(barFlow), "/namespace");
|
||||
assertSame(barFlow, child.getFlowDefinition("/namespace/bar"));
|
||||
assertEquals("bar", child.getFlowDefinition("/namespace/bar").getId());
|
||||
}
|
||||
|
||||
private static class FooFlow implements FlowDefinition {
|
||||
private String id = "foo";
|
||||
|
||||
|
||||
public AttributeMap getAttributes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getCaption() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public StateDefinition getStartState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public StateDefinition getState(String id) throws IllegalArgumentException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class BarFlow implements FlowDefinition {
|
||||
private String id = "bar";
|
||||
|
||||
public AttributeMap getAttributes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package org.springframework.webflow.definition.registry;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class FlowPathUtilsTests extends TestCase {
|
||||
|
||||
public void testNamespaceWithNoSlash() {
|
||||
assertEquals("Incorrect namespace", "", FlowPathUtils.extractFlowNamespace("flow"));
|
||||
}
|
||||
|
||||
public void testNamespaceWithSlash() {
|
||||
assertEquals("Incorrect namespace", "", FlowPathUtils.extractFlowNamespace("/flow"));
|
||||
}
|
||||
|
||||
public void testNamespaceWithNamespace() {
|
||||
assertEquals("Incorrect namespace", "/namespace", FlowPathUtils.extractFlowNamespace("/namespace/flow"));
|
||||
}
|
||||
|
||||
public void teswtNamespaceWithComplexNamespace() {
|
||||
assertEquals("Incorrect namespace", "/complex/namespace", FlowPathUtils.extractFlowNamespace("/complex/namespace/flow"));
|
||||
}
|
||||
|
||||
public void testNamespaceEmpty() {
|
||||
try {
|
||||
FlowPathUtils.extractFlowNamespace("");
|
||||
fail("Should have detected empty input");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testNamespaceWhitespace() {
|
||||
try {
|
||||
FlowPathUtils.extractFlowNamespace(" ");
|
||||
fail("Should have detected empty input");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testNamespaceNull() {
|
||||
try {
|
||||
FlowPathUtils.extractFlowNamespace(null);
|
||||
fail("Should have detected empty input");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testIdWithNoSlash() {
|
||||
assertEquals("Incorrect id", "flow", FlowPathUtils.extractFlowId("flow"));
|
||||
}
|
||||
|
||||
public void testIdWithSlash() {
|
||||
assertEquals("Incorrect id", "flow", FlowPathUtils.extractFlowId("/flow"));
|
||||
}
|
||||
|
||||
public void testIdWithNamespace() {
|
||||
assertEquals("Incorrect id", "flow", FlowPathUtils.extractFlowId("/namespace/flow"));
|
||||
}
|
||||
|
||||
public void testIdWithComplexNamespace() {
|
||||
assertEquals("Incorrect id", "flow", FlowPathUtils.extractFlowId("/complex/namespace/flow"));
|
||||
}
|
||||
|
||||
public void testIdEmpty() {
|
||||
try {
|
||||
FlowPathUtils.extractFlowId("");
|
||||
fail("Should have detected empty input");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testIdWhitespace() {
|
||||
try {
|
||||
FlowPathUtils.extractFlowId(" ");
|
||||
fail("Should have detected empty input");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testIdNull() {
|
||||
try {
|
||||
FlowPathUtils.extractFlowId(null);
|
||||
fail("Should have detected empty input");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testPathWithEmptyNamespace() {
|
||||
assertEquals("Incorrect path", "/flow", FlowPathUtils.buildFlowPath("", "flow"));
|
||||
}
|
||||
|
||||
public void testPathWithNamespace() {
|
||||
assertEquals("Incorrect path", "/namespace/flow", FlowPathUtils.buildFlowPath("/namespace", "flow"));
|
||||
}
|
||||
|
||||
public void testPathWithComplexNamespace() {
|
||||
assertEquals("Incorrect path", "/complex/namespace/flow", FlowPathUtils.buildFlowPath("/complex/namespace", "flow"));
|
||||
}
|
||||
|
||||
public void testPathWithNullNamespace() {
|
||||
try {
|
||||
FlowPathUtils.buildFlowPath(null, "flow");
|
||||
fail("Should have detected empty input");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testPathWithEmptyId() {
|
||||
try {
|
||||
FlowPathUtils.buildFlowPath("", "");
|
||||
fail("Should have detected empty input");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testPathWithWhitespaceId() {
|
||||
try {
|
||||
FlowPathUtils.buildFlowPath("", " ");
|
||||
fail("Should have detected empty input");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testPathWithNullId() {
|
||||
try {
|
||||
FlowPathUtils.buildFlowPath("", null);
|
||||
fail("Should have detected empty input");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ public class XmlFlowRegistrarTests extends TestCase {
|
||||
registrar.addLocation(new ClassPathResource("flow.xml", getClass()));
|
||||
registrar.registerFlowDefinitions(registry);
|
||||
assertEquals(1, registry.getFlowDefinitionCount());
|
||||
assertEquals("flow", registry.getFlowDefinitions()[0].getId());
|
||||
assertEquals("flow", registry.getFlowDefinition("flow").getId());
|
||||
}
|
||||
|
||||
public void testAddResource() {
|
||||
@@ -46,6 +46,6 @@ public class XmlFlowRegistrarTests extends TestCase {
|
||||
registrar.addResource(new FlowDefinitionResource("foo", new ClassPathResource("flow.xml", getClass())));
|
||||
registrar.registerFlowDefinitions(registry);
|
||||
assertEquals(1, registry.getFlowDefinitionCount());
|
||||
assertEquals("foo", registry.getFlowDefinitions()[0].getId());
|
||||
assertEquals("foo", registry.getFlowDefinition("foo").getId());
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,9 @@ public class XmlFlowRegistryFactoryBeanTests extends TestCase {
|
||||
factoryBean.setFlowLocations(locations);
|
||||
factoryBean.setBeanFactory(new StaticListableBeanFactory());
|
||||
factoryBean.afterPropertiesSet();
|
||||
FlowDefinitionRegistry registry = (FlowDefinitionRegistry)factoryBean.getObject();
|
||||
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) factoryBean.getObject();
|
||||
assertEquals(1, registry.getFlowDefinitionCount());
|
||||
assertEquals("flow", registry.getFlowDefinitions()[0].getId());
|
||||
assertEquals("flow", registry.getFlowDefinition("flow").getId());
|
||||
}
|
||||
|
||||
public void testCreateFromDefinitions() throws Exception {
|
||||
@@ -42,8 +42,8 @@ public class XmlFlowRegistryFactoryBeanTests extends TestCase {
|
||||
factoryBean.setFlowDefinitions(properties);
|
||||
factoryBean.setBeanFactory(new StaticListableBeanFactory());
|
||||
factoryBean.afterPropertiesSet();
|
||||
FlowDefinitionRegistry registry = (FlowDefinitionRegistry)factoryBean.getObject();
|
||||
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) factoryBean.getObject();
|
||||
assertEquals(1, registry.getFlowDefinitionCount());
|
||||
assertEquals("foo", registry.getFlowDefinitions()[0].getId());
|
||||
assertEquals("foo", registry.getFlowDefinition("foo").getId());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user