retrofitting flow refresh checking

responsibility of builder now...
This commit is contained in:
Keith Donald
2008-03-23 03:40:43 +00:00
parent 485a83eea7
commit 41ab629934
14 changed files with 196 additions and 165 deletions

View File

@@ -19,7 +19,7 @@ import org.springframework.webflow.definition.registry.FlowDefinitionRegistryImp
import org.springframework.webflow.engine.builder.FlowAssembler;
import org.springframework.webflow.engine.builder.FlowBuilder;
import org.springframework.webflow.engine.builder.FlowBuilderContext;
import org.springframework.webflow.engine.builder.RefreshableFlowDefinitionHolder;
import org.springframework.webflow.engine.builder.DefaultFlowHolder;
import org.springframework.webflow.engine.builder.model.FlowModelFlowBuilder;
import org.springframework.webflow.engine.builder.support.FlowBuilderContextImpl;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
@@ -127,7 +127,7 @@ class FlowRegistryFactoryBean implements FactoryBean, InitializingBean {
FlowBuilderContext builderContext = new FlowBuilderContextImpl(flowResource.getId(), flowResource
.getAttributes(), flowRegistry, flowBuilderServices);
FlowAssembler assembler = new FlowAssembler(builder, builderContext);
return new RefreshableFlowDefinitionHolder(assembler);
return new DefaultFlowHolder(assembler);
}
private FlowDefinitionResource createResource(FlowLocation location) {
@@ -148,7 +148,7 @@ class FlowRegistryFactoryBean implements FactoryBean, InitializingBean {
}
private FlowBuilder createFlowBuilder(FlowDefinitionResource resource) {
return new FlowModelFlowBuilder(createFlowModelHolder(resource), resource.getPath());
return new FlowModelFlowBuilder(createFlowModelHolder(resource));
}
private FlowModelHolder createFlowModelHolder(FlowDefinitionResource resource) {

View File

@@ -15,15 +15,11 @@
*/
package org.springframework.webflow.engine.builder;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.registry.FlowDefinitionConstructionException;
import org.springframework.webflow.definition.registry.FlowDefinitionHolder;
import org.springframework.webflow.util.ResourceHolder;
/**
* A flow definition holder that can detect changes on an underlying flow definition resource and refresh that resource
@@ -39,9 +35,9 @@ import org.springframework.webflow.util.ResourceHolder;
*
* @author Keith Donald
*/
public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
public class DefaultFlowHolder implements FlowDefinitionHolder {
private static final Log logger = LogFactory.getLog(RefreshableFlowDefinitionHolder.class);
private static final Log logger = LogFactory.getLog(DefaultFlowHolder.class);
/**
* The flow definition assembled by this assembler.
@@ -53,12 +49,6 @@ 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.
*/
private long lastModified;
/**
* A flag indicating whether or not this holder is in the middle of the assembly process.
*/
@@ -69,7 +59,7 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
* assembly, on initial use and on any resource change or refresh.
* @param assembler the flow assembler to use
*/
public RefreshableFlowDefinitionHolder(FlowAssembler assembler) {
public DefaultFlowHolder(FlowAssembler assembler) {
this.assembler = assembler;
}
@@ -83,11 +73,13 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
return getFlowBuilder().getFlow();
}
if (flowDefinition == null) {
lastModified = calculateLastModified();
logger.debug("Assembling the flow definition for the first time");
logger.debug("Assembling the flow for the first time");
assembleFlow();
} else {
refreshIfChanged();
if (getFlowBuilder().hasFlowChanged()) {
logger.debug("The flow has changed; reassembling...");
assembleFlow();
}
}
return flowDefinition;
}
@@ -98,29 +90,6 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
// internal helpers
/**
* Helper that retrieves the last modified date by querying the backing flow resource.
* @return the last modified date, or 0L if it could not be retrieved
*/
private long calculateLastModified() {
if (getFlowBuilder() instanceof ResourceHolder) {
Resource resource = ((ResourceHolder) getFlowBuilder()).getResource();
try {
long lastModified = resource.getFile().lastModified();
if (logger.isDebugEnabled()) {
logger.debug("Flow definition [" + resource + "] was last modified on " + lastModified);
}
return lastModified;
} catch (IOException e) {
// ignore, last modified checks not supported
}
}
return 0L;
}
/**
* Assemble the held flow definition, delegating to the configured FlowAssembler (director).
*/
private void assembleFlow() throws FlowDefinitionConstructionException {
try {
assembling = true;
@@ -132,23 +101,6 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
}
}
/**
* Reassemble the flow if its underlying resource has changed.
*/
private void refreshIfChanged() {
long calculatedLastModified = calculateLastModified();
if (calculatedLastModified > lastModified) {
if (logger.isDebugEnabled()) {
logger.debug("Refreshing flow definition [" + flowDefinition.getId() + "]");
}
assembleFlow();
lastModified = calculatedLastModified;
}
}
/**
* Returns the flow builder that actually builds the Flow definition.
*/
private FlowBuilder getFlowBuilder() {
return assembler.getFlowBuilder();
}

View File

@@ -125,4 +125,11 @@ public interface FlowBuilder {
* @throws FlowBuilderException an exception occurred building this flow
*/
public void dispose() throws FlowBuilderException;
/**
* As the underlying flow resource managed by this builder changed since the last build occurred?
* @return true if changed, false if not
*/
public boolean hasFlowChanged();
}

View File

@@ -87,33 +87,30 @@ import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.ScopeType;
import org.springframework.webflow.execution.ViewFactory;
import org.springframework.webflow.security.SecurityRule;
import org.springframework.webflow.util.ResourceHolder;
public class FlowModelFlowBuilder extends AbstractFlowBuilder implements ResourceHolder {
/**
* Builds a runtime {@link Flow} definition object from a {@link FlowModel}.
*
* @author Keith Donald
*/
public class FlowModelFlowBuilder extends AbstractFlowBuilder {
private FlowModelHolder flowModelHolder;
private FlowModel flowModel;
private Resource resource;
private LocalFlowBuilderContext localFlowBuilderContext;
public FlowModelFlowBuilder(FlowModelHolder flowModelHolder) {
this.flowModelHolder = flowModelHolder;
}
public FlowModelFlowBuilder(FlowModelHolder flowModelHolder, Resource resource) {
this.flowModelHolder = flowModelHolder;
this.resource = resource;
}
/**
* Initialize this builder. This could cause the builder to open a stream to an externalized resource representing
* the flow definition, for example.
* @throws FlowBuilderException an exception occurred building the flow
*/
public void doInit() throws FlowBuilderException {
protected void doInit() throws FlowBuilderException {
flowModel = flowModelHolder.getFlowModel();
initLocalFlowContext();
}
@@ -152,7 +149,7 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder implements Resourc
*/
public void buildStates() throws FlowBuilderException {
if (flowModel.getStates() == null) {
throw new FlowBuilderException("At least one state is required to build a flow definition");
throw new FlowBuilderException("At least one state is required to build a Flow");
}
for (Iterator it = flowModel.getStates().iterator(); it.hasNext();) {
AbstractStateModel state = (AbstractStateModel) it.next();
@@ -208,22 +205,20 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder implements Resourc
parseExceptionHandlers(flowModel.getExceptionHandlers(), flowModel.getGlobalTransitions()));
}
public boolean hasFlowChanged() {
return flowModelHolder.hasFlowModelChanged();
}
/**
* Shutdown the builder, releasing any resources it holds. A new flow construction process should start with another
* call to the {@link #init(FlowBuilderContext)} method.
* @throws FlowBuilderException an exception occurred building this flow
*/
public void doDispose() throws FlowBuilderException {
protected void doDispose() throws FlowBuilderException {
flowModel = null;
setLocalContext(null);
}
// implementing resource holder
public Resource getResource() {
return resource;
}
// subclassing hooks
protected Flow createFlow() {
@@ -266,14 +261,15 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder implements Resourc
private Resource[] parseContextResources(List beanImports) {
if (beanImports != null && !beanImports.isEmpty()) {
if (getResource() == null) {
throw new FlowBuilderException("A resource must be defined in order to load bean-imports");
Resource flowResource = flowModelHolder.getFlowModelResource();
if (flowResource == null) {
throw new FlowBuilderException("The FlowModel must be Resource in order to load bean-imports");
}
List resources = new ArrayList(beanImports.size());
for (Iterator it = getFlowModel().getBeanImports().iterator(); it.hasNext();) {
BeanImportModel beanImport = (BeanImportModel) it.next();
try {
resources.add(getResource().createRelative(beanImport.getResource()));
resources.add(flowResource.createRelative(beanImport.getResource()));
} catch (IOException e) {
throw new FlowBuilderException("Could not access flow-relative artifact resource '"
+ beanImport.getResource() + "'", e);
@@ -305,7 +301,10 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder implements Resourc
flowContext.getBeanFactory().setParentBeanFactory(parent);
}
}
flowContext.setResourceLoader(new FlowRelativeResourceLoader(resource));
Resource flowResource = flowModelHolder.getFlowModelResource();
if (flowResource != null) {
flowContext.setResourceLoader(new FlowRelativeResourceLoader(flowResource));
}
if (JdkVersion.isAtLeastJava15()) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(flowContext);
}

View File

@@ -105,6 +105,10 @@ public abstract class AbstractFlowBuilder implements FlowBuilder {
doDispose();
}
public boolean hasFlowChanged() {
return false;
}
/**
* Flow builder destruction hook. Does nothing by default. May be overridden by subclasses.
*/

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.webflow.engine.model.builder;
import org.springframework.core.io.Resource;
import org.springframework.webflow.engine.model.FlowModel;
/**
@@ -67,4 +68,17 @@ public interface FlowModelBuilder {
* @throws FlowModelBuilderException an exception occurred disposing this flow
*/
public void dispose() throws FlowModelBuilderException;
/**
* Returns true if the underlying flow model has changed since the last call to {@link #init()}.
* @return true if the flow model has changed
*/
public boolean hasFlowModelChanged();
/**
* Get the underlying flow model resource accessed to build this flow model. Returns null if this builder does not
* construct the flow model from a resource.
* @return the flow model resource
*/
public Resource getFlowModelResource();
}

View File

@@ -78,6 +78,8 @@ public class XmlFlowModelBuilder implements FlowModelBuilder, ResourceHolder {
private FlowModel flowModel;
private long lastModifiedTimestamp;
/**
* Create a new XML flow model builder that will parse the XML document at the specified resource location and use
* the provided locator to access parent flow models.
@@ -89,6 +91,23 @@ public class XmlFlowModelBuilder implements FlowModelBuilder, ResourceHolder {
this.modelLocator = modelLocator;
}
public Resource getFlowModelResource() {
return resource;
}
public boolean hasFlowModelChanged() {
try {
long lastModified = resource.getFile().lastModified();
if (lastModified > lastModifiedTimestamp) {
return true;
} else {
return false;
}
} catch (IOException e) {
return false;
}
}
/**
* Sets the loader that will load the XML-based flow definition document. Optional, defaults to
* {@link DefaultDocumentLoader}.
@@ -106,6 +125,7 @@ public class XmlFlowModelBuilder implements FlowModelBuilder, ResourceHolder {
public void init() throws FlowModelBuilderException {
try {
document = documentLoader.loadDocument(resource);
lastModifiedTimestamp = getResource().getFile().lastModified();
} catch (IOException e) {
throw new FlowModelBuilderException("Could not access the XML flow definition resource at " + resource, e);
} catch (ParserConfigurationException e) {

View File

@@ -15,15 +15,12 @@
*/
package org.springframework.webflow.engine.model.registry;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.webflow.engine.model.FlowModel;
import org.springframework.webflow.engine.model.builder.FlowModelBuilder;
import org.springframework.webflow.engine.model.builder.FlowModelBuilderException;
import org.springframework.webflow.util.ResourceHolder;
/**
* A flow model holder that can detect changes on an underlying flow model resource and refresh that resource
@@ -55,7 +52,7 @@ public class DefaultFlowModelHolder implements FlowModelHolder {
/**
* The flow model builder.
*/
private FlowModelBuilder builder;
private FlowModelBuilder flowModelBuilder;
/**
* A last modified date for the backing flow definition resource, used to support automatic reassembly on resource
@@ -66,21 +63,11 @@ public class DefaultFlowModelHolder implements FlowModelHolder {
/**
* Creates a new refreshable flow model holder that uses the configured assembler (GOF director) to drive flow
* assembly, on initial use and on any resource change or refresh.
* @param builder the flow model builder to use
* @param flowModelBuilder the flow model builder to use
* @param flowModelId the identifier of the flow model
*/
public DefaultFlowModelHolder(FlowModelBuilder builder, String flowModelId) {
this.builder = builder;
this.flowModelId = flowModelId;
}
/**
* Creates a new static flow model holder
* @param flowModel the flow model to hold
* @param flowModelId the identifier of the flow model
*/
public DefaultFlowModelHolder(FlowModel flowModel, String flowModelId) {
this.flowModel = flowModel;
public DefaultFlowModelHolder(FlowModelBuilder flowModelBuilder, String flowModelId) {
this.flowModelBuilder = flowModelBuilder;
this.flowModelId = flowModelId;
}
@@ -90,77 +77,41 @@ public class DefaultFlowModelHolder implements FlowModelHolder {
public synchronized FlowModel getFlowModel() throws FlowModelConstructionException {
if (flowModel == null) {
lastModified = calculateLastModified();
logger.debug("Assembling the flow model for the first time");
assembleFlow();
assembleFlowModel();
} else {
refreshIfChanged();
if (flowModelBuilder.hasFlowModelChanged()) {
assembleFlowModel();
}
}
return flowModel;
}
public Resource getFlowModelResource() {
return flowModelBuilder.getFlowModelResource();
}
public boolean hasFlowModelChanged() {
return flowModelBuilder.hasFlowModelChanged();
}
public synchronized void refresh() throws FlowModelConstructionException {
assembleFlow();
assembleFlowModel();
}
// internal helpers
/**
* Helper that retrieves the last modified date by querying the backing flow resource.
* @return the last modified date, or 0L if it could not be retrieved
*/
private long calculateLastModified() {
if (getFlowModelBuilder() instanceof ResourceHolder) {
Resource resource = ((ResourceHolder) getFlowModelBuilder()).getResource();
try {
long lastModified = resource.getFile().lastModified();
if (logger.isDebugEnabled()) {
logger.debug("Flow definition [" + resource + "] was last modified on " + lastModified);
}
return lastModified;
} catch (IOException e) {
// ignore, last modified checks not supported
}
}
return 0L;
}
/**
* Assemble the held flow definition, delegating to the configured FlowAssembler (director).
*/
private void assembleFlow() throws FlowModelConstructionException {
private void assembleFlowModel() throws FlowModelConstructionException {
try {
builder.init();
builder.build();
flowModel = builder.getFlowModel();
flowModelBuilder.init();
flowModelBuilder.build();
flowModel = flowModelBuilder.getFlowModel();
} catch (FlowModelBuilderException e) {
throw new FlowModelConstructionException(flowModelId, e);
} finally {
builder.dispose();
flowModelBuilder.dispose();
}
}
/**
* Reassemble the flow if its underlying resource has changed.
*/
private void refreshIfChanged() {
long calculatedLastModified = calculateLastModified();
if (calculatedLastModified > lastModified) {
if (logger.isDebugEnabled()) {
logger.debug("Refreshing flow definition [" + flowModelId + "]");
}
assembleFlow();
lastModified = calculatedLastModified;
}
}
/**
* Returns the flow builder that actually builds the Flow definition.
*/
private FlowModelBuilder getFlowModelBuilder() {
return builder;
}
public String toString() {
return "'" + getFlowModelId() + "'";
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.webflow.engine.model.registry;
import org.springframework.core.io.Resource;
import org.springframework.webflow.engine.model.FlowModel;
/**
@@ -42,10 +43,24 @@ public interface FlowModelHolder {
*/
public FlowModel getFlowModel() throws FlowModelConstructionException;
/**
* Has the underlying flow model changed since it was last accessed via a call to {@link #getFlowModel()}.
* @return true if yes, false if not
*/
public boolean hasFlowModelChanged();
/**
* Returns the underlying resource defining the flow model. Will return null if the flow model did not originate
* from a file-based resource.
* @return the flow model resource, or null
*/
public Resource getFlowModelResource();
/**
* Refresh the flow model 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 FlowModelConstructionException if there is a problem constructing the target flow model
*/
public void refresh() throws FlowModelConstructionException;
}

View File

@@ -81,7 +81,7 @@ public abstract class AbstractXmlFlowExecutionTests extends AbstractExternalized
FlowModelBuilder modelBuilder = new XmlFlowModelBuilder(resource.getPath(), flowModelRegistry);
FlowModelHolder modelHolder = new DefaultFlowModelHolder(modelBuilder, resource.getId());
flowModelRegistry.registerFlowModel(modelHolder);
return new FlowModelFlowBuilder(modelHolder, resource.getPath()) {
return new FlowModelFlowBuilder(modelHolder) {
protected void registerFlowBeans(ConfigurableBeanFactory flowBeanFactory) {
registerMockFlowBeans(flowBeanFactory);
}

View File

@@ -11,13 +11,13 @@ import org.springframework.webflow.engine.builder.support.AbstractFlowBuilder;
import org.springframework.webflow.test.MockFlowBuilderContext;
import org.springframework.webflow.util.ResourceHolder;
public class RefreshableFlowDefinitionHolderTests extends TestCase {
private RefreshableFlowDefinitionHolder holder;
public class DefaultFlowHolderTests extends TestCase {
private DefaultFlowHolder holder;
private FlowAssembler assembler;
protected void setUp() {
FlowAssembler assembler = new FlowAssembler(new SimpleFlowBuilder(), new MockFlowBuilderContext("flowId"));
holder = new RefreshableFlowDefinitionHolder(assembler);
holder = new DefaultFlowHolder(assembler);
}
public void testGetFlowDefinition() {
@@ -28,7 +28,7 @@ public class RefreshableFlowDefinitionHolderTests extends TestCase {
public void testGetFlowDefinitionWithChangesRefreshed() {
assembler = new FlowAssembler(new ChangeDetectableFlowBuilder(), new MockFlowBuilderContext("flowId"));
holder = new RefreshableFlowDefinitionHolder(assembler);
holder = new DefaultFlowHolder(assembler);
FlowDefinition flow = holder.getFlowDefinition();
flow = holder.getFlowDefinition();
assertEquals("flowId", flow.getId());

View File

@@ -4,6 +4,7 @@ import junit.framework.TestCase;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.webflow.action.ExternalRedirectAction;
import org.springframework.webflow.action.FlowDefinitionRedirectAction;
import org.springframework.webflow.core.collection.LocalAttributeMap;
@@ -28,6 +29,7 @@ import org.springframework.webflow.engine.model.ViewStateModel;
import org.springframework.webflow.engine.model.builder.xml.XmlFlowModelBuilder;
import org.springframework.webflow.engine.model.builder.xml.XmlFlowModelBuilderTests;
import org.springframework.webflow.engine.model.registry.DefaultFlowModelHolder;
import org.springframework.webflow.engine.model.registry.FlowModelConstructionException;
import org.springframework.webflow.engine.model.registry.FlowModelHolder;
import org.springframework.webflow.engine.model.registry.FlowModelRegistryImpl;
import org.springframework.webflow.engine.support.ActionExecutingViewFactory;
@@ -325,7 +327,7 @@ public class FlowModelFlowBuilderTests extends TestCase {
}
private Flow getFlow(FlowModel model) {
FlowModelHolder holder = new DefaultFlowModelHolder(model, "flow");
FlowModelHolder holder = new StaticFlowModelHolder(model);
FlowModelFlowBuilder builder = new FlowModelFlowBuilder(holder);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
return assembler.assembleFlow();
@@ -334,9 +336,37 @@ public class FlowModelFlowBuilderTests extends TestCase {
private Flow getFlow(ClassPathResource resource) {
FlowModelHolder holder = new DefaultFlowModelHolder(new XmlFlowModelBuilder(resource,
new FlowModelRegistryImpl()), "flow");
FlowModelFlowBuilder builder = new FlowModelFlowBuilder(holder, resource);
FlowModelFlowBuilder builder = new FlowModelFlowBuilder(holder);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
return assembler.assembleFlow();
}
private static class StaticFlowModelHolder implements FlowModelHolder {
private FlowModel model;
public StaticFlowModelHolder(FlowModel model) {
this.model = model;
}
public FlowModel getFlowModel() throws FlowModelConstructionException {
return model;
}
public String getFlowModelId() {
return "flow";
}
public Resource getFlowModelResource() {
return null;
}
public boolean hasFlowModelChanged() {
return false;
}
public void refresh() throws FlowModelConstructionException {
}
}
}

View File

@@ -58,6 +58,14 @@ public class DefaultFlowModelHolderTests extends TestCase {
// no-op
}
public Resource getFlowModelResource() {
return null;
}
public boolean hasFlowModelChanged() {
return false;
}
}
public class ChangeDetectableFlowBuilder extends SimpleFlowBuilder implements ResourceHolder {

View File

@@ -2,6 +2,7 @@ package org.springframework.webflow.engine.model.registry;
import junit.framework.TestCase;
import org.springframework.core.io.Resource;
import org.springframework.webflow.engine.model.FlowModel;
public class FlowModelRegistryImplTests extends TestCase {
@@ -27,20 +28,20 @@ public class FlowModelRegistryImplTests extends TestCase {
}
public void testRegisterFlow() {
registry.registerFlowModel(new DefaultFlowModelHolder(fooFlow, "foo"));
registry.registerFlowModel(new StaticFlowModelHolder(fooFlow, "foo"));
assertEquals(fooFlow, registry.getFlowModel("foo"));
}
public void testRegisterFlowSameIds() {
registry.registerFlowModel(new DefaultFlowModelHolder(fooFlow, "foo"));
registry.registerFlowModel(new StaticFlowModelHolder(fooFlow, "foo"));
FlowModel newFlow = new FlowModel();
registry.registerFlowModel(new DefaultFlowModelHolder(newFlow, "foo"));
registry.registerFlowModel(new StaticFlowModelHolder(newFlow, "foo"));
assertSame(newFlow, registry.getFlowModel("foo"));
}
public void testRegisterMultipleFlows() {
registry.registerFlowModel(new DefaultFlowModelHolder(fooFlow, "foo"));
registry.registerFlowModel(new DefaultFlowModelHolder(barFlow, "bar"));
registry.registerFlowModel(new StaticFlowModelHolder(fooFlow, "foo"));
registry.registerFlowModel(new StaticFlowModelHolder(barFlow, "bar"));
assertEquals(fooFlow, registry.getFlowModel("foo"));
assertEquals(barFlow, registry.getFlowModel("bar"));
}
@@ -50,9 +51,39 @@ public class FlowModelRegistryImplTests extends TestCase {
FlowModelRegistryImpl child = new FlowModelRegistryImpl();
child.setParent(registry);
FlowModel fooFlow = new FlowModel();
child.registerFlowModel(new DefaultFlowModelHolder(fooFlow, "foo"));
child.registerFlowModel(new StaticFlowModelHolder(fooFlow, "foo"));
assertSame(fooFlow, child.getFlowModel("foo"));
assertEquals(barFlow, child.getFlowModel("bar"));
}
private static class StaticFlowModelHolder implements FlowModelHolder {
private FlowModel model;
private String id;
public StaticFlowModelHolder(FlowModel model, String id) {
this.model = model;
this.id = id;
}
public FlowModel getFlowModel() throws FlowModelConstructionException {
return model;
}
public String getFlowModelId() {
return id;
}
public Resource getFlowModelResource() {
return null;
}
public boolean hasFlowModelChanged() {
return false;
}
public void refresh() throws FlowModelConstructionException {
}
}
}