spring webflow 2.x - 如何将alwaysGenerateNewNextKey设置为false以使用单个快照

时间:2017-05-02 04:13:30

标签: spring-webflow spring-webflow-2

团队,我们正在将spring webflow从1.x升级到2.4。

在SWF 1.x中,我们只对整个会话使用了一个flowExecutionKey,并且还禁用了后退导航。

在SWF 2.4中,我们想要有相同的行为,

我们可以通过将max-execution-snapshots配置为0来禁用快照,如下所示

<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
    <webflow:flow-execution-repository max-executions="1" max-execution-snapshots="0"/>
</webflow:flow-executor>

但是在导航时,当我们调查此问题时,快照ID会不断增加(like e1s1, e1s2 ...),这会导致问题,

AbstractFlowExecutionRepository具有以下字段,这有助于在true时为每个导航生成新的snapshotId,

alwaysGenerateNewNextKey默认情况下为真。

在SWF 1.x中有一个选项可以通过RepositoryType.SINGLEKEY设置为false,但我们找不到任何等效的方法在SWF 2.4中将此值设置为false。

如果有办法覆盖"alwaysGenerateNewNextKey"

,请提供帮助

2 个答案:

答案 0 :(得分:0)

现在我知道这很难看,但是你可以做到这一点:

制作您自己的FlowExecutorFactoryBeanFlowElementAttribute副本,将其命名为MyFlowExecutorFactoryBeanMyFlowElementAttribute(将MyFlowExecutorFactoryBean中的引用从FlowElementAttribute更改为{ {1}})。

MyFlowElementAttribute中的

MyFlowExecutorFactoryBean上设置了您想要的属性,例如executionRepository

结果如下:

setAlwaysGenerateNewNextKey(true)

现在在您的网络流配置中使用它:

public class MyFlowExecutorFactoryBean implements FactoryBean<FlowExecutor>, ApplicationContextAware, BeanClassLoaderAware, InitializingBean {

    private static final String ALWAYS_REDIRECT_ON_PAUSE = "alwaysRedirectOnPause";

    private static final String REDIRECT_IN_SAME_STATE = "redirectInSameState";

    private FlowDefinitionLocator flowDefinitionLocator;

    private Integer maxFlowExecutions;

    private Integer maxFlowExecutionSnapshots;

    private Set<MyFlowElementAttribute> flowExecutionAttributes;

    private FlowExecutionListenerLoader flowExecutionListenerLoader;

    private ConversationManager conversationManager;

    private ConversionService conversionService;

    private FlowExecutor flowExecutor;

    private MvcEnvironment environment;

    private ClassLoader classLoader;

    /**
     * Sets the flow definition locator that will locate flow definitions needed for execution. Typically also a
     * {@link FlowDefinitionRegistry}. Required.
     * @param flowDefinitionLocator the flow definition locator (registry)
     */
    public void setFlowDefinitionLocator(FlowDefinitionLocator flowDefinitionLocator) {
        this.flowDefinitionLocator = flowDefinitionLocator;
    }

    /**
     * Set the maximum number of allowed flow executions allowed per user.
     */
    public void setMaxFlowExecutions(int maxFlowExecutions) {
        this.maxFlowExecutions = maxFlowExecutions;
    }

    /**
     * Set the maximum number of history snapshots allowed per flow execution.
     */
    public void setMaxFlowExecutionSnapshots(int maxFlowExecutionSnapshots) {
        this.maxFlowExecutionSnapshots = maxFlowExecutionSnapshots;
    }

    /**
     * Sets the system attributes that apply to flow executions launched by the executor created by this factory.
     * Execution attributes may affect flow execution behavior.
     * @param flowExecutionAttributes the flow execution system attributes
     */
    public void setFlowExecutionAttributes(Set<MyFlowElementAttribute> flowExecutionAttributes) {
        this.flowExecutionAttributes = flowExecutionAttributes;
    }

    /**
     * Sets the strategy for loading the listeners that will observe executions of a flow definition. Allows full
     * control over what listeners should apply to executions of a flow definition launched by the executor created by
     * this factory.
     */
    public void setFlowExecutionListenerLoader(FlowExecutionListenerLoader flowExecutionListenerLoader) {
        this.flowExecutionListenerLoader = flowExecutionListenerLoader;
    }

    /**
     * Sets the service type that manages conversations and effectively controls how state is stored physically when a
     * flow execution is paused.
     */
    public void setConversationManager(ConversationManager conversationManager) {
        this.conversationManager = conversationManager;
    }

    // implementing ApplicationContextAware

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        environment = MvcEnvironment.environmentFor(applicationContext);
    }

    // implement BeanClassLoaderAware

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    // implementing InitializingBean

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(flowDefinitionLocator, "The flow definition locator property is required");
        if (conversionService == null) {
            conversionService = new DefaultConversionService();
        }
        MutableAttributeMap<Object> executionAttributes = createFlowExecutionAttributes();
        FlowExecutionImplFactory executionFactory = createFlowExecutionFactory(executionAttributes);
        DefaultFlowExecutionRepository executionRepository = createFlowExecutionRepository(executionFactory);
        executionRepository.setMaxSnapshots(0);
        executionRepository.setAlwaysGenerateNewNextKey(false);
        executionFactory.setExecutionKeyFactory(executionRepository);
        flowExecutor = new FlowExecutorImpl(flowDefinitionLocator, executionFactory, executionRepository);
    }

    // implementing FactoryBean

    @Override
    public Class<?> getObjectType() {
        return FlowExecutor.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public FlowExecutor getObject() throws Exception {
        return flowExecutor;
    }

    private MutableAttributeMap<Object> createFlowExecutionAttributes() {
        LocalAttributeMap<Object> executionAttributes = new LocalAttributeMap<Object>();
        if (flowExecutionAttributes != null) {
            for (MyFlowElementAttribute attribute : flowExecutionAttributes) {
                executionAttributes.put(attribute.getName(), getConvertedValue(attribute));
            }
        }
        putDefaultFlowExecutionAttributes(executionAttributes);
        return executionAttributes;
    }

    private void putDefaultFlowExecutionAttributes(LocalAttributeMap<Object> executionAttributes) {
        if (!executionAttributes.contains(ALWAYS_REDIRECT_ON_PAUSE)) {
            Boolean redirect = environment != MvcEnvironment.PORTLET;
            executionAttributes.put(ALWAYS_REDIRECT_ON_PAUSE, redirect);
        }
        if (!executionAttributes.contains(REDIRECT_IN_SAME_STATE)) {
            Boolean redirect = environment != MvcEnvironment.PORTLET;
            executionAttributes.put(REDIRECT_IN_SAME_STATE, redirect);
        }
    }

    private DefaultFlowExecutionRepository createFlowExecutionRepository(FlowExecutionFactory executionFactory) {
        ConversationManager conversationManager = createConversationManager();
        FlowExecutionSnapshotFactory snapshotFactory = createFlowExecutionSnapshotFactory(executionFactory);
        DefaultFlowExecutionRepository rep = new DefaultFlowExecutionRepository(conversationManager, snapshotFactory);
        if (maxFlowExecutionSnapshots != null) {
            rep.setMaxSnapshots(maxFlowExecutionSnapshots);
        }
        return rep;
    }

    private ConversationManager createConversationManager() {
        if (conversationManager == null) {
            conversationManager = new SessionBindingConversationManager();
            if (maxFlowExecutions != null) {
                ((SessionBindingConversationManager) conversationManager).setMaxConversations(maxFlowExecutions);
            }
        }
        return this.conversationManager;
    }

    private FlowExecutionSnapshotFactory createFlowExecutionSnapshotFactory(FlowExecutionFactory executionFactory) {
        if (maxFlowExecutionSnapshots != null && maxFlowExecutionSnapshots == 0) {
            maxFlowExecutionSnapshots = 1;
            return new SimpleFlowExecutionSnapshotFactory(executionFactory, flowDefinitionLocator);
        } else {
            return new SerializedFlowExecutionSnapshotFactory(executionFactory, flowDefinitionLocator);
        }
    }

    private FlowExecutionImplFactory createFlowExecutionFactory(AttributeMap<Object> executionAttributes) {
        FlowExecutionImplFactory executionFactory = new FlowExecutionImplFactory();
        executionFactory.setExecutionAttributes(executionAttributes);
        if (flowExecutionListenerLoader != null) {
            executionFactory.setExecutionListenerLoader(flowExecutionListenerLoader);
        }
        return executionFactory;
    }

    // utility methods

    private Object getConvertedValue(MyFlowElementAttribute attribute) {
        if (attribute.needsTypeConversion()) {
            Class<?> targetType = fromStringToClass(attribute.getType());
            ConversionExecutor converter = conversionService.getConversionExecutor(String.class, targetType);
            return converter.execute(attribute.getValue());
        } else {
            return attribute.getValue();
        }
    }

    private Class<?> fromStringToClass(String name) {
        Class<?> clazz = conversionService.getClassForAlias(name);
        if (clazz != null) {
            return clazz;
        } else {
            try {
                return ClassUtils.forName(name, classLoader);
            } catch (ClassNotFoundException e) {
                throw new IllegalArgumentException("Unable to load class '" + name + "'");
            }
        }
    }

    class MyFlowElementAttribute {

        /**
         * The name of the attribute.
         */
        private String name;

        /**
         * The value of the attribute before type-conversion.
         */
        private String value;

        /**
         * The attribute type, optional, but necessary for type conversion.
         */
        private String type;

        public MyFlowElementAttribute(String name, String value, String type) {
            Assert.hasText(name, "The name is required");
            Assert.hasText(value, "The value is required");
            this.name = name;
            this.value = value;
            this.type = type;
        }

        public String getName() {
            return name;
        }

        public String getValue() {
            return value;
        }

        public String getType() {
            return type;
        }

        public boolean needsTypeConversion() {
            return type != null && type.length() > 0;
        }
    }
}

我还没有完全测试它,但它应该可以工作

答案 1 :(得分:0)

谢谢,

我还修改了它,扩展了DefaultFlowExecutionRepository,如果可能,请查看是否可以,并希望它对某人有帮助,

<bean id="flowExecutionRepository" class="com.custom.module.SingleFlowExecutionRepository">
    <constructor-arg index="0" ref="conversationManager"/>
    <constructor-arg index="1" ref="snapshotFactory"/>
</bean>

<bean id="flowExecutor" class="org.springframework.webflow.executor.FlowExecutorImpl">
    <constructor-arg index="0" ref="flowRegistry"/>
    <constructor-arg index="1" ref="executionFactory" name="executionFactory"/>
    <constructor-arg index="2" ref="flowExecutionRepository" name="executionRepository"/>
</bean>

并且

公共类SingleFlowExecutionRepository扩展了DefaultFlowExecutionRepository {

public SingleFlowExecutionRepository(ConversationManager conversationManager,
        FlowExecutionSnapshotFactory snapshotFactory) {
    super(conversationManager, snapshotFactory);        
    /**
     * Set to maintain single snapshot for a session.
     */
    super.setAlwaysGenerateNewNextKey(false);
}