Spring Webflow 2.x - как установить AlwaysGenerateNewNextKey в false, чтобы использовать один снимок
Команда, мы обновляем весенний веб-поток с 1.x до 2.4.
В SWF 1.x мы использовали только один flowExecutionKey для всего сеанса, а также отключили обратную навигацию.
В SWF 2.4 мы хотели иметь такое же поведение,
Мы смогли отключить снимок, настроив для max-execute-snapshots значение 0, как показано ниже,
<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<webflow:flow-execution-repository max-executions="1" max-execution-snapshots="0"/>
</webflow:flow-executor>
Но на навигации идентификатор снимка постоянно увеличивается (like e1s1, e1s2 ...)
что вызывает проблему, когда мы исследуем это,
AbstractFlowExecutionRepository
имеет поле ниже, которое помогает генерировать новый snapshotId для каждой навигации, когда true,
alwaysGenerateNewNextKey
что верно по умолчанию.
В SWF 1.x была возможность установить значение false через RepositoryType.SINGLEKEY
, но мы не смогли найти какой-либо эквивалентный способ установить это значение в false в SWF 2.4.
Пожалуйста, помогите, если есть способ перезаписать "alwaysGenerateNewNextKey"
2 ответа
Спасибо,
Я также исправил это с расширением 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 extends DefaultFlowExecutionRepository {
public SingleFlowExecutionRepository(ConversationManager conversationManager,
FlowExecutionSnapshotFactory snapshotFactory) {
super(conversationManager, snapshotFactory);
/**
* Set to maintain single snapshot for a session.
*/
super.setAlwaysGenerateNewNextKey(false);
}
Теперь я знаю, что это безобразно, но вот что вы можете сделать:
Сделайте свою собственную копию FlowExecutorFactoryBean
а также FlowElementAttribute
, Назови их MyFlowExecutorFactoryBean
а также MyFlowElementAttribute
(изменить ссылки в MyFlowExecutorFactoryBean
от FlowElementAttribute
в 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;
}
}
}
Теперь используйте это в вашей конфигурации веб-потока:
<bean id="flowExecutor" class="com.example.MyFlowExecutorFactoryBean ">
<property name="flowDefinitionLocator" ref="flowRegistry" />
<property name="flowExecutionListenerLoader">
<bean class="org.springframework.webflow.config.FlowExecutionListenerLoaderFactoryBean">
<property name="listeners">
<map>
<entry key-ref="yourFirstListener" value="*" />
<entry key-ref="yourSecondListener" value="*" />
</map>
</property>
</bean>
</property>
</bean>
Я не полностью проверил это, но это должно работать