diff --git a/spring-webflow/changelog.txt b/spring-webflow/changelog.txt
index b19e0462..eaba990c 100644
--- a/spring-webflow/changelog.txt
+++ b/spring-webflow/changelog.txt
@@ -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.
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionHolder.java b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionHolder.java
index 06d11bdf..2f179eb0 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionHolder.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionHolder.java
@@ -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 id of the flow definition held by this holder.
- * This is a lightweight 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 id of the flow definition held by this holder. This is a lightweight 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;
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionLocator.java b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionLocator.java
index ccefa49f..b93b21b3 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionLocator.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionLocator.java
@@ -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
- * id.
+ * A runtime service locator interface for retrieving flow definitions by id.
*
- * 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 id.
- * @param id the flow definition id
+ * Lookup the flow definition with the specified path.
+ * @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;
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistry.java b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistry.java
index 4a7eb360..40950e1f 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistry.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistry.java
@@ -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);
+
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryImpl.java
index 3c807752..9f12b525 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryImpl.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryImpl.java
@@ -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();
+ }
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryMBean.java b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryMBean.java
index 085dda94..a6a60dda 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryMBean.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryMBean.java
@@ -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.
*
- * 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
- * MBeanExporter 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 MBeanExporter to export a flow registry to an MBeanServer:
+ *
*
* <!-- 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> *- * 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
id
- * 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 path 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;
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowPathUtils.java b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowPathUtils.java
new file mode 100644
index 00000000..f93aa284
--- /dev/null
+++ b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowPathUtils.java
@@ -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;
+ }
+}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/StaticFlowDefinitionHolder.java b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/StaticFlowDefinitionHolder.java
index 3728ace4..d26dea95 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/StaticFlowDefinitionHolder.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/StaticFlowDefinitionHolder.java
@@ -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
*/
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/RefreshableFlowDefinitionHolder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/RefreshableFlowDefinitionHolder.java
index 85abb5dc..3d187d05 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/RefreshableFlowDefinitionHolder.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/RefreshableFlowDefinitionHolder.java
@@ -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.
* - * This class is threadsafe. + * This class is thread-safe. *
- * 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 abstract 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 abstract world of {@link FlowDefinition flow definitions} * with the concrete 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; diff --git a/spring-webflow/src/test/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryImplTests.java b/spring-webflow/src/test/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryImplTests.java index 3f6227a1..20662b87 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryImplTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryImplTests.java @@ -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; } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/definition/registry/FlowPathUtilsTests.java b/spring-webflow/src/test/java/org/springframework/webflow/definition/registry/FlowPathUtilsTests.java new file mode 100644 index 00000000..6eb24759 --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/definition/registry/FlowPathUtilsTests.java @@ -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) { + } + } +} diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistrarTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistrarTests.java index 16a89502..2b54b550 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistrarTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistrarTests.java @@ -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()); } } \ No newline at end of file diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistryFactoryBeanTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistryFactoryBeanTests.java index cf402e37..c616c130 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistryFactoryBeanTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistryFactoryBeanTests.java @@ -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()); } }