Overhaul annotation and listener processing
- This is state 1 for these changes. - This is a stage one of fixes for #126 and #138, thus not giving full support not complete features. - Add new annotations in a side of OnTransition reflecting stuff from a StateMachineListener. - Prepare and modify call chains so that we have preliminary support(not feature ready) for these so that existing tests pass. - Add new helper to handle some annotation and spel call caching. - Enhance StateContext to better suit these needs.
This commit is contained in:
@@ -15,29 +15,44 @@
|
||||
*/
|
||||
package org.springframework.statemachine;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* {@code StateContext} is representing a current context used in
|
||||
* {@code StateContext} is representing of a current context used in
|
||||
* various stages in a state machine execution. These include for example
|
||||
* {@link Transition}s, {@link Action}s and {@link Guard}s order to get access
|
||||
* to event headers and {@link ExtendedState}.
|
||||
* <p>
|
||||
* Context really is not a current state of a state machine but
|
||||
* more like a snapshot of where state machine is when this context
|
||||
* is passed to various methods.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public interface StateContext<S, E> {
|
||||
|
||||
/**
|
||||
* Gets the message associated with a context. Message may be null if transition
|
||||
* is not triggered by a signal.
|
||||
*
|
||||
* @return the message
|
||||
*/
|
||||
Message<E> getMessage();
|
||||
|
||||
/**
|
||||
* Gets the event associated with a context. Event may be null if transition
|
||||
* is not triggered by a signal.
|
||||
*
|
||||
*
|
||||
* @return the event
|
||||
*/
|
||||
E getEvent();
|
||||
|
||||
|
||||
/**
|
||||
* Gets the event message headers.
|
||||
*
|
||||
@@ -75,4 +90,29 @@ public interface StateContext<S, E> {
|
||||
*/
|
||||
StateMachine<S, E> getStateMachine();
|
||||
|
||||
/**
|
||||
* Gets the source state of this context. Generally source
|
||||
* is where a state machine is coming from which may be different
|
||||
* than what the transition source is.
|
||||
*
|
||||
* @return the source state
|
||||
*/
|
||||
State<S,E> getSource();
|
||||
|
||||
/**
|
||||
* Gets the tarter state of this context. Generally target
|
||||
* is where a state machine going to which may be different
|
||||
* than what the transition target is.
|
||||
*
|
||||
* @return the target state
|
||||
*/
|
||||
State<S,E> getTarget();
|
||||
|
||||
/**
|
||||
* Gets the exception associated with a context.
|
||||
*
|
||||
* @return the exception
|
||||
*/
|
||||
Exception getException();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface ExtendedStateVariable {
|
||||
|
||||
@AliasFor("key")
|
||||
String value() default "";
|
||||
|
||||
@AliasFor("value")
|
||||
String key() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
|
||||
/**
|
||||
* Indicates that a method is candidate to be called when event
|
||||
* is not accepted by a state machine.
|
||||
* <p>
|
||||
* A method annotated with @OnEventNotAccepted may accept a parameter of type
|
||||
* {@link ExtendedState} or {@link Map} if map argument is itself is annotated
|
||||
* with {@link EventHeaders}.
|
||||
* <p>
|
||||
* Return value can be anything and is effectively discarded.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface OnEventNotAccepted {
|
||||
|
||||
/**
|
||||
* The events.
|
||||
*
|
||||
* @return the events
|
||||
*/
|
||||
String[] event() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
|
||||
/**
|
||||
* Indicates that a method is candidate to be called when {@link ExtendedState}
|
||||
* is changed.
|
||||
* <p>
|
||||
* Return value can be anything and is effectively discarded.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Target({ ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface OnExtendedStateChanged {
|
||||
|
||||
/**
|
||||
* The extended state variable keys.
|
||||
*
|
||||
* @return The extended state variable keys
|
||||
*/
|
||||
String[] key() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.state.State;
|
||||
|
||||
/**
|
||||
* Indicates that a method is candidate to be called when {@link State}
|
||||
* is changed.
|
||||
* <p>
|
||||
* A method annotated with @OnStateChanged may accept a parameter of type
|
||||
* {@link ExtendedState} or {@link Map} if map argument is itself is annotated
|
||||
* with {@link EventHeaders}.
|
||||
* <p>
|
||||
* Return value can be anything and is effectively discarded.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface OnStateChanged {
|
||||
|
||||
/**
|
||||
* The source states.
|
||||
*
|
||||
* @return the source states.
|
||||
*/
|
||||
String[] source() default {};
|
||||
|
||||
/**
|
||||
* The target states.
|
||||
*
|
||||
* @return the target states.
|
||||
*/
|
||||
String[] target() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.state.State;
|
||||
|
||||
/**
|
||||
* Indicates that a method is candidate to be called when {@link State}
|
||||
* is entered.
|
||||
* <p>
|
||||
* A method annotated with @OnStateChanged may accept a parameter of type
|
||||
* {@link ExtendedState} or {@link Map} if map argument is itself is annotated
|
||||
* with {@link EventHeaders}.
|
||||
* <p>
|
||||
* Return value can be anything and is effectively discarded.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface OnStateEntry {
|
||||
|
||||
/**
|
||||
* The source states.
|
||||
*
|
||||
* @return the source states.
|
||||
*/
|
||||
String[] source() default {};
|
||||
|
||||
/**
|
||||
* The target states.
|
||||
*
|
||||
* @return the target states.
|
||||
*/
|
||||
String[] target() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.state.State;
|
||||
|
||||
/**
|
||||
* Indicates that a method is candidate to be called when {@link State}
|
||||
* is exited.
|
||||
* <p>
|
||||
* A method annotated with @OnStateChanged may accept a parameter of type
|
||||
* {@link ExtendedState} or {@link Map} if map argument is itself is annotated
|
||||
* with {@link EventHeaders}.
|
||||
* <p>
|
||||
* Return value can be anything and is effectively discarded.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface OnStateExit {
|
||||
|
||||
/**
|
||||
* The source states.
|
||||
*
|
||||
* @return the source states.
|
||||
*/
|
||||
String[] source() default {};
|
||||
|
||||
/**
|
||||
* The target states.
|
||||
*
|
||||
* @return the target states.
|
||||
*/
|
||||
String[] target() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
|
||||
/**
|
||||
* Indicates that a method is candidate to be called when state machine
|
||||
* has been entered in error it cannot recover.
|
||||
* <p>
|
||||
* A method annotated with @OnStateMachineError may accept a parameter of type
|
||||
* {@link ExtendedState} or {@link Map} if map argument is itself is annotated
|
||||
* with {@link EventHeaders}.
|
||||
* <p>
|
||||
* Return value can be anything and is effectively discarded.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface OnStateMachineError {
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
|
||||
/**
|
||||
* Indicates that a method is a candidate to be called when state machine
|
||||
* is started.
|
||||
* <p>
|
||||
* A method annotated with @OnStateMachineStart may accept a parameter of type
|
||||
* {@link StateMachine}.
|
||||
* <p>
|
||||
* Return value can be anything and is effectively discarded.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface OnStateMachineStart {
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that a method is a candidate to be called when state machine
|
||||
* is stopped.
|
||||
* <p>
|
||||
* Return value can be anything and is effectively discarded.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface OnStateMachineStop {
|
||||
}
|
||||
@@ -21,15 +21,41 @@ import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* Indicates that a method is candidate to be called with a {@link Transition}.
|
||||
* <p>
|
||||
* A method annotated with @OnTransition may accept a parameter of type
|
||||
* {@link ExtendedState} or {@link Map} if map argument is itself is annotated
|
||||
* with {@link EventHeaders}.
|
||||
* <p>
|
||||
* Return value can be anything and is effectively discarded.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface OnTransition {
|
||||
|
||||
/**
|
||||
* The source states.
|
||||
*
|
||||
* @return the source states.
|
||||
*/
|
||||
String[] source() default {};
|
||||
|
||||
/**
|
||||
* The target states.
|
||||
*
|
||||
* @return the target states.
|
||||
*/
|
||||
String[] target() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* Indicates that a method is candidate to be called with a {@link Transition}.
|
||||
* <p>
|
||||
* A method annotated with @OnTransitionEnd may accept a parameter of type
|
||||
* {@link ExtendedState} or {@link Map} if map argument is itself is annotated
|
||||
* with {@link EventHeaders}.
|
||||
* <p>
|
||||
* Return value can be anything and is effectively discarded.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface OnTransitionEnd {
|
||||
|
||||
/**
|
||||
* The source states.
|
||||
*
|
||||
* @return the source states.
|
||||
*/
|
||||
String[] source() default {};
|
||||
|
||||
/**
|
||||
* The target states.
|
||||
*
|
||||
* @return the target states.
|
||||
*/
|
||||
String[] target() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* Indicates that a method is candidate to be called with a {@link Transition}.
|
||||
* <p>
|
||||
* A method annotated with @OnTransitionStart may accept a parameter of type
|
||||
* {@link ExtendedState} or {@link Map} if map argument is itself is annotated
|
||||
* with {@link EventHeaders}.
|
||||
* <p>
|
||||
* Return value can be anything and is effectively discarded.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface OnTransitionStart {
|
||||
|
||||
/**
|
||||
* The source states.
|
||||
*
|
||||
* @return the source states.
|
||||
*/
|
||||
String[] source() default {};
|
||||
|
||||
/**
|
||||
* The target states.
|
||||
*
|
||||
* @return the target states.
|
||||
*/
|
||||
String[] target() default {};
|
||||
|
||||
}
|
||||
@@ -23,15 +23,8 @@ import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.annotation.OrderUtils;
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
|
||||
/**
|
||||
* Post-processor for Methods annotated with {@link OnTransition}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class StateMachineActivatorAnnotationPostProcessor implements MethodAnnotationPostProcessor<OnTransition>{
|
||||
public class StateMachineActivatorAnnotationPostProcessor<T extends Annotation> implements MethodAnnotationPostProcessor<T>{
|
||||
|
||||
protected final BeanFactory beanFactory;
|
||||
|
||||
@@ -40,8 +33,8 @@ public class StateMachineActivatorAnnotationPostProcessor implements MethodAnnot
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcess(Class<?> beanClass, Object bean, String beanName, Method method, OnTransition metaAnnotation, Annotation annotation) {
|
||||
StateMachineHandler<Object, Object> handler = new StateMachineOnTransitionHandler<Object, Object>(beanClass, bean, method, metaAnnotation, annotation);
|
||||
public Object postProcess(Class<?> beanClass, Object bean, String beanName, Method method, T metaAnnotation, Annotation annotation) {
|
||||
StateMachineHandler<T, Object, Object> handler = new StateMachineHandler<T, Object, Object>(beanClass, bean, method, metaAnnotation, annotation);
|
||||
|
||||
Integer order = findOrder(bean, method);
|
||||
if (order != null) {
|
||||
|
||||
@@ -41,7 +41,17 @@ import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.statemachine.annotation.OnEventNotAccepted;
|
||||
import org.springframework.statemachine.annotation.OnExtendedStateChanged;
|
||||
import org.springframework.statemachine.annotation.OnStateChanged;
|
||||
import org.springframework.statemachine.annotation.OnStateEntry;
|
||||
import org.springframework.statemachine.annotation.OnStateExit;
|
||||
import org.springframework.statemachine.annotation.OnStateMachineError;
|
||||
import org.springframework.statemachine.annotation.OnStateMachineStart;
|
||||
import org.springframework.statemachine.annotation.OnStateMachineStop;
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
import org.springframework.statemachine.annotation.OnTransitionEnd;
|
||||
import org.springframework.statemachine.annotation.OnTransitionStart;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -93,7 +103,28 @@ public class StateMachineAnnotationPostProcessor implements BeanPostProcessor, B
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
postProcessors.put(OnTransition.class, new StateMachineActivatorAnnotationPostProcessor(beanFactory));
|
||||
postProcessors.put(OnTransition.class,
|
||||
new StateMachineActivatorAnnotationPostProcessor<OnTransition>(beanFactory));
|
||||
postProcessors.put(OnTransitionStart.class,
|
||||
new StateMachineActivatorAnnotationPostProcessor<OnTransitionStart>(beanFactory));
|
||||
postProcessors.put(OnTransitionEnd.class,
|
||||
new StateMachineActivatorAnnotationPostProcessor<OnTransitionEnd>(beanFactory));
|
||||
postProcessors.put(OnStateChanged.class,
|
||||
new StateMachineActivatorAnnotationPostProcessor<OnStateChanged>(beanFactory));
|
||||
postProcessors.put(OnStateEntry.class,
|
||||
new StateMachineActivatorAnnotationPostProcessor<OnStateEntry>(beanFactory));
|
||||
postProcessors.put(OnStateExit.class,
|
||||
new StateMachineActivatorAnnotationPostProcessor<OnStateExit>(beanFactory));
|
||||
postProcessors.put(OnStateMachineStart.class,
|
||||
new StateMachineActivatorAnnotationPostProcessor<OnStateMachineStart>(beanFactory));
|
||||
postProcessors.put(OnStateMachineStop.class,
|
||||
new StateMachineActivatorAnnotationPostProcessor<OnStateMachineStop>(beanFactory));
|
||||
postProcessors.put(OnEventNotAccepted.class,
|
||||
new StateMachineActivatorAnnotationPostProcessor<OnEventNotAccepted>(beanFactory));
|
||||
postProcessors.put(OnStateMachineError.class,
|
||||
new StateMachineActivatorAnnotationPostProcessor<OnStateMachineError>(beanFactory));
|
||||
postProcessors.put(OnExtendedStateChanged.class,
|
||||
new StateMachineActivatorAnnotationPostProcessor<OnExtendedStateChanged>(beanFactory));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,51 +146,42 @@ public class StateMachineAnnotationPostProcessor implements BeanPostProcessor, B
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
|
||||
|
||||
for (Annotation annotation : annotations) {
|
||||
|
||||
Annotation metaAnnotation = null;
|
||||
for (Class<? extends Annotation> ppa : postProcessors.keySet()) {
|
||||
Annotation a = AnnotationUtils.getAnnotation(annotation,ppa);
|
||||
if (a != null && annotation.getClass().equals(a.getClass())) {
|
||||
metaAnnotation = a;
|
||||
} else {
|
||||
metaAnnotation = a;
|
||||
}
|
||||
for (Class<? extends Annotation> ppa : postProcessors.keySet()) {
|
||||
Annotation metaAnnotation = AnnotationUtils.findAnnotation(method, ppa);
|
||||
if (metaAnnotation == null) {
|
||||
continue;
|
||||
}
|
||||
for (Annotation a : AnnotationUtils.getAnnotations(method)) {
|
||||
MethodAnnotationPostProcessor postProcessor = metaAnnotation != null ? postProcessors
|
||||
.get(metaAnnotation.annotationType()) : null;
|
||||
if (postProcessor != null && shouldCreateHandler(a)) {
|
||||
Object result = postProcessor.postProcess(beanClass, bean, beanName, method, metaAnnotation, a);
|
||||
if (result != null && result instanceof StateMachineHandler) {
|
||||
String endpointBeanName = generateBeanName(beanName, method, a.annotationType());
|
||||
|
||||
MethodAnnotationPostProcessor postProcessor = metaAnnotation != null ? postProcessors
|
||||
.get(metaAnnotation.annotationType()) : null;
|
||||
|
||||
if (postProcessor != null && shouldCreateHandler(annotation)) {
|
||||
Object result = postProcessor.postProcess(beanClass, bean, beanName, method, metaAnnotation, annotation);
|
||||
|
||||
if (result != null && result instanceof StateMachineHandler) {
|
||||
String endpointBeanName = generateBeanName(beanName, method, annotation.annotationType());
|
||||
|
||||
if (result instanceof BeanNameAware) {
|
||||
((BeanNameAware) result).setBeanName(endpointBeanName);
|
||||
}
|
||||
beanFactory.registerSingleton(endpointBeanName, result);
|
||||
if (result instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) result).setBeanFactory(beanFactory);
|
||||
}
|
||||
if (result instanceof InitializingBean) {
|
||||
try {
|
||||
((InitializingBean) result).afterPropertiesSet();
|
||||
} catch (Exception e) {
|
||||
throw new BeanInitializationException("failed to initialize annotated component", e);
|
||||
if (result instanceof BeanNameAware) {
|
||||
((BeanNameAware) result).setBeanName(endpointBeanName);
|
||||
}
|
||||
}
|
||||
if (result instanceof Lifecycle) {
|
||||
lifecycles.add((Lifecycle) result);
|
||||
if (result instanceof SmartLifecycle && ((SmartLifecycle) result).isAutoStartup()) {
|
||||
((SmartLifecycle) result).start();
|
||||
beanFactory.registerSingleton(endpointBeanName, result);
|
||||
if (result instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) result).setBeanFactory(beanFactory);
|
||||
}
|
||||
if (result instanceof InitializingBean) {
|
||||
try {
|
||||
((InitializingBean) result).afterPropertiesSet();
|
||||
} catch (Exception e) {
|
||||
throw new BeanInitializationException("failed to initialize annotated component", e);
|
||||
}
|
||||
}
|
||||
if (result instanceof Lifecycle) {
|
||||
lifecycles.add((Lifecycle) result);
|
||||
if (result instanceof SmartLifecycle && ((SmartLifecycle) result).isAutoStartup()) {
|
||||
((SmartLifecycle) result).start();
|
||||
}
|
||||
}
|
||||
if (result instanceof ApplicationListener) {
|
||||
listeners.add((ApplicationListener) result);
|
||||
}
|
||||
}
|
||||
if (result instanceof ApplicationListener) {
|
||||
listeners.add((ApplicationListener) result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.statemachine.processor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
@@ -28,15 +29,16 @@ import org.springframework.statemachine.annotation.WithStateMachine;
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <T> the type of annotation
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class StateMachineHandler<S, E> implements Ordered {
|
||||
public class StateMachineHandler<T extends Annotation, S, E> implements Ordered {
|
||||
|
||||
private final Class<?> beanClass;
|
||||
|
||||
private final StateMachineRuntimeProcessor<?, S, E> processor;
|
||||
|
||||
private final T metaAnnotation;
|
||||
private final Annotation annotation;
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
/**
|
||||
@@ -44,10 +46,12 @@ public class StateMachineHandler<S, E> implements Ordered {
|
||||
*
|
||||
* @param beanClass the bean class
|
||||
* @param target the target bean
|
||||
* @param metaAnnotation the meta annotation
|
||||
* @param annotation the annotation
|
||||
* @param method the method
|
||||
*/
|
||||
public StateMachineHandler(Class<?> beanClass, Object target, Method method) {
|
||||
this(beanClass, new MethodInvokingStateMachineRuntimeProcessor<Object, S, E>(target, method));
|
||||
public StateMachineHandler(Class<?> beanClass, Object target, Method method, T metaAnnotation, Annotation annotation) {
|
||||
this(beanClass, metaAnnotation, annotation, new MethodInvokingStateMachineRuntimeProcessor<T, S, E>(target, method));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,31 +60,55 @@ public class StateMachineHandler<S, E> implements Ordered {
|
||||
* @param beanClass the bean class
|
||||
* @param target the target bean
|
||||
* @param methodName the method name
|
||||
* @param metaAnnotation the meta annotation
|
||||
* @param annotation the annotation
|
||||
*/
|
||||
public StateMachineHandler(Class<?> beanClass, Object target, String methodName) {
|
||||
this(beanClass, new MethodInvokingStateMachineRuntimeProcessor<Object, S, E>(target, methodName));
|
||||
public StateMachineHandler(Class<?> beanClass, Object target, String methodName, T metaAnnotation, Annotation annotation) {
|
||||
this(beanClass, metaAnnotation, annotation, new MethodInvokingStateMachineRuntimeProcessor<T, S, E>(target, methodName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new container handler.
|
||||
*
|
||||
* @param <T> the generic type
|
||||
* @param beanClass the bean class
|
||||
* @param metaAnnotation the meta annotation
|
||||
* @param annotation the annotation
|
||||
* @param processor the processor
|
||||
*/
|
||||
public <T> StateMachineHandler(Class<?> beanClass, MethodInvokingStateMachineRuntimeProcessor<T, S, E> processor) {
|
||||
public StateMachineHandler(Class<?> beanClass, T metaAnnotation, Annotation annotation,
|
||||
MethodInvokingStateMachineRuntimeProcessor<T, S, E> processor) {
|
||||
this.beanClass = beanClass;
|
||||
this.processor = processor;
|
||||
this.metaAnnotation = metaAnnotation;
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the meta annotation.
|
||||
*
|
||||
* @return the meta annotation
|
||||
*/
|
||||
public T getMetaAnnotation() {
|
||||
return metaAnnotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the annotation.
|
||||
*
|
||||
* @return the annotation
|
||||
*/
|
||||
public Annotation getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bean class.
|
||||
*
|
||||
*
|
||||
* @return the bean class
|
||||
*/
|
||||
public Class<?> getBeanClass() {
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.processor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.annotation.OnEventNotAccepted;
|
||||
import org.springframework.statemachine.annotation.OnExtendedStateChanged;
|
||||
import org.springframework.statemachine.annotation.OnStateChanged;
|
||||
import org.springframework.statemachine.annotation.OnStateEntry;
|
||||
import org.springframework.statemachine.annotation.OnStateExit;
|
||||
import org.springframework.statemachine.annotation.OnStateMachineError;
|
||||
import org.springframework.statemachine.annotation.OnStateMachineStart;
|
||||
import org.springframework.statemachine.annotation.OnStateMachineStop;
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
import org.springframework.statemachine.annotation.OnTransitionEnd;
|
||||
import org.springframework.statemachine.annotation.OnTransitionStart;
|
||||
import org.springframework.statemachine.annotation.WithStateMachine;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.support.StateMachineUtils;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Helper class which is used from a StateMachineObjectSupport to ease handling
|
||||
* of StateMachineHandlers and provides needed caching so that a runtime calls
|
||||
* are fast. Also provides dedicated methods for each annotated methods so that
|
||||
* parameters are handled accordingly.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class StateMachineHandlerCallHelper<S, E> implements InitializingBean, BeanFactoryAware {
|
||||
|
||||
private final Log log = LogFactory.getLog(StateMachineHandlerCallHelper.class);
|
||||
private final Map<String, List<CacheEntry>> cache = new HashMap<>();
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (!(beanFactory instanceof ListableBeanFactory)) {
|
||||
log.info("Beanfactory is not instance of ListableBeanFactory, was " + beanFactory + " thus Disabling handlers.");
|
||||
return;
|
||||
}
|
||||
for (StateMachineHandler<? extends Annotation, S, E> handler : beanFactory.getBeansOfType(StateMachineHandler.class).values()) {
|
||||
Annotation annotation = handler.getAnnotation();
|
||||
Annotation metaAnnotation = handler.getMetaAnnotation();
|
||||
WithStateMachine withStateMachine = AnnotationUtils.findAnnotation(handler.getBeanClass(),
|
||||
WithStateMachine.class);
|
||||
String statemachineBeanName = withStateMachine.name();
|
||||
String key = metaAnnotation.annotationType().getName() + statemachineBeanName;
|
||||
List<CacheEntry> list = cache.get(key);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
cache.put(key, list);
|
||||
}
|
||||
list.add(new CacheEntry(handler, annotation, metaAnnotation));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
Assert.state(beanFactory instanceof ListableBeanFactory,
|
||||
"Bean factory must be instance of ListableBeanFactory, was " + beanFactory);
|
||||
this.beanFactory = (ListableBeanFactory)beanFactory;
|
||||
}
|
||||
|
||||
public void callOnStateChanged(String stateMachineId, Transition<S,E> transition, Message<E> message, StateContext<S, E> stateContext) {
|
||||
List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
|
||||
String cacheKey = OnStateChanged.class.getName() + stateMachineId;
|
||||
List<CacheEntry> list = cache.get(cacheKey);
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
for (CacheEntry entry : list) {
|
||||
if (annotationHandlerSourceTargetMatch((String[]) AnnotationUtils.getValue(entry.metaAnnotation, "source"),
|
||||
(String[]) AnnotationUtils.getValue(entry.metaAnnotation, "target"), entry.annotation, stateContext.getSource(),
|
||||
stateContext.getTarget())) {
|
||||
handlersList.add(entry.handler);
|
||||
}
|
||||
}
|
||||
getStateMachineHandlerResults(handlersList, stateContext);
|
||||
}
|
||||
|
||||
public void callOnStateEntry(String stateMachineId, Transition<S,E> transition, Message<E> message, StateContext<S, E> stateContext) {
|
||||
List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
|
||||
String cacheKey = OnStateEntry.class.getName() + stateMachineId;
|
||||
List<CacheEntry> list = cache.get(cacheKey);
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
for (CacheEntry entry : list) {
|
||||
if (annotationHandlerSourceTargetMatch((String[]) AnnotationUtils.getValue(entry.metaAnnotation, "source"),
|
||||
(String[]) AnnotationUtils.getValue(entry.metaAnnotation, "target"), entry.annotation, stateContext.getSource(),
|
||||
stateContext.getTarget())) {
|
||||
handlersList.add(entry.handler);
|
||||
}
|
||||
}
|
||||
getStateMachineHandlerResults(handlersList, stateContext);
|
||||
}
|
||||
|
||||
public void callOnStateExit(String stateMachineId, Transition<S,E> transition, Message<E> message, StateContext<S, E> stateContext) {
|
||||
List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
|
||||
String cacheKey = OnStateExit.class.getName() + stateMachineId;
|
||||
List<CacheEntry> list = cache.get(cacheKey);
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
for (CacheEntry entry : list) {
|
||||
if (annotationHandlerSourceTargetMatch((String[]) AnnotationUtils.getValue(entry.metaAnnotation, "source"),
|
||||
(String[]) AnnotationUtils.getValue(entry.metaAnnotation, "target"), entry.annotation, stateContext.getSource(),
|
||||
stateContext.getTarget())) {
|
||||
handlersList.add(entry.handler);
|
||||
}
|
||||
}
|
||||
getStateMachineHandlerResults(handlersList, stateContext);
|
||||
}
|
||||
|
||||
public void callOnEventNotAccepted(String stateMachineId, StateContext<S, E> stateContext) {
|
||||
List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
|
||||
String cacheKey = OnEventNotAccepted.class.getName() + stateMachineId;
|
||||
List<CacheEntry> list = cache.get(cacheKey);
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
for (CacheEntry entry : list) {
|
||||
handlersList.add(entry.handler);
|
||||
}
|
||||
getStateMachineHandlerResults(handlersList, stateContext);
|
||||
}
|
||||
|
||||
|
||||
public void callOnTransitionStart(String stateMachineId, Transition<S,E> transition, Message<E> message, StateContext<S, E> stateContext) {
|
||||
List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
|
||||
String cacheKey = OnTransitionStart.class.getName() + stateMachineId;
|
||||
List<CacheEntry> list = cache.get(cacheKey);
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
for (CacheEntry entry : list) {
|
||||
if (annotationHandlerSourceTargetMatch((String[]) AnnotationUtils.getValue(entry.metaAnnotation, "source"),
|
||||
(String[]) AnnotationUtils.getValue(entry.metaAnnotation, "target"), entry.annotation, transition.getSource(),
|
||||
transition.getTarget())) {
|
||||
handlersList.add(entry.handler);
|
||||
}
|
||||
}
|
||||
getStateMachineHandlerResults(handlersList, stateContext);
|
||||
}
|
||||
|
||||
public void callOnTransition(String stateMachineId, Transition<S,E> transition, Message<E> message, StateContext<S, E> stateContext) {
|
||||
List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
|
||||
String cacheKey = OnTransition.class.getName() + stateMachineId;
|
||||
List<CacheEntry> list = cache.get(cacheKey);
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
for (CacheEntry entry : list) {
|
||||
if (annotationHandlerSourceTargetMatch((String[]) AnnotationUtils.getValue(entry.metaAnnotation, "source"),
|
||||
(String[]) AnnotationUtils.getValue(entry.metaAnnotation, "target"), entry.annotation, transition.getSource(),
|
||||
transition.getTarget())) {
|
||||
handlersList.add(entry.handler);
|
||||
}
|
||||
}
|
||||
getStateMachineHandlerResults(handlersList, stateContext);
|
||||
}
|
||||
|
||||
public void callOnTransitionEnd(String stateMachineId, Transition<S,E> transition, Message<E> message, StateContext<S, E> stateContext) {
|
||||
List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
|
||||
String cacheKey = OnTransitionEnd.class.getName() + stateMachineId;
|
||||
List<CacheEntry> list = cache.get(cacheKey);
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
for (CacheEntry entry : list) {
|
||||
if (annotationHandlerSourceTargetMatch((String[]) AnnotationUtils.getValue(entry.metaAnnotation, "source"),
|
||||
(String[]) AnnotationUtils.getValue(entry.metaAnnotation, "target"), entry.annotation, transition.getSource(),
|
||||
transition.getTarget())) {
|
||||
handlersList.add(entry.handler);
|
||||
}
|
||||
}
|
||||
getStateMachineHandlerResults(handlersList, stateContext);
|
||||
}
|
||||
|
||||
public void callOnStateMachineStart(String stateMachineId, StateContext<S, E> stateContext) {
|
||||
List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
|
||||
String cacheKey = OnStateMachineStart.class.getName() + stateMachineId;
|
||||
List<CacheEntry> list = cache.get(cacheKey);
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
for (CacheEntry entry : list) {
|
||||
handlersList.add(entry.handler);
|
||||
}
|
||||
getStateMachineHandlerResults(handlersList, stateContext);
|
||||
}
|
||||
|
||||
public void callOnStateMachineStop(String stateMachineId, StateContext<S, E> stateContext) {
|
||||
List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
|
||||
String cacheKey = OnStateMachineStop.class.getName() + stateMachineId;
|
||||
List<CacheEntry> list = cache.get(cacheKey);
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
for (CacheEntry entry : list) {
|
||||
handlersList.add(entry.handler);
|
||||
}
|
||||
getStateMachineHandlerResults(handlersList, stateContext);
|
||||
}
|
||||
|
||||
public void callOnStateMachineError(String stateMachineId, StateContext<S, E> stateContext) {
|
||||
List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
|
||||
String cacheKey = OnStateMachineError.class.getName() + stateMachineId;
|
||||
List<CacheEntry> list = cache.get(cacheKey);
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
for (CacheEntry entry : list) {
|
||||
handlersList.add(entry.handler);
|
||||
}
|
||||
getStateMachineHandlerResults(handlersList, stateContext);
|
||||
}
|
||||
|
||||
public void callOnExtendedStateChanged(String stateMachineId, Object key, Object value, StateContext<S, E> stateContext) {
|
||||
List<StateMachineHandler<? extends Annotation, S, E>> handlersList = new ArrayList<StateMachineHandler<? extends Annotation, S, E>>();
|
||||
String cacheKey = OnExtendedStateChanged.class.getName() + stateMachineId;
|
||||
List<CacheEntry> list = cache.get(cacheKey);
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
for (CacheEntry entry : list) {
|
||||
if (annotationHandlerVariableMatch(entry.metaAnnotation, key)) {
|
||||
handlersList.add(entry.handler);
|
||||
}
|
||||
}
|
||||
getStateMachineHandlerResults(handlersList, stateContext);
|
||||
}
|
||||
|
||||
private boolean annotationHandlerVariableMatch(Annotation annotation, Object key) {
|
||||
boolean handle = false;
|
||||
Map<String, Object> annotationAttributes = AnnotationUtils.getAnnotationAttributes(annotation);
|
||||
Object object = annotationAttributes.get("key");
|
||||
Collection<String> scoll = StateMachineUtils.toStringCollection(object);
|
||||
if (!scoll.isEmpty()) {
|
||||
if (StateMachineUtils.containsAtleastOne(scoll, StateMachineUtils.toStringCollection(key))) {
|
||||
handle = true;
|
||||
}
|
||||
} else {
|
||||
handle = true;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
private boolean annotationHandlerSourceTargetMatch(String[] msources, String[] mtargets, Annotation methodAnnotation,
|
||||
State<S, E> sourceState, State<S, E> targetState) {
|
||||
Map<String, Object> annotationAttributes = AnnotationUtils.getAnnotationAttributes(methodAnnotation);
|
||||
Object source = annotationAttributes.get("source");
|
||||
Object target = annotationAttributes.get("target");
|
||||
|
||||
Collection<String> scoll = StateMachineUtils.toStringCollection(source);
|
||||
if (scoll.isEmpty() && msources != null) {
|
||||
scoll = Arrays.asList(msources);
|
||||
}
|
||||
Collection<String> tcoll = StateMachineUtils.toStringCollection(target);
|
||||
if (tcoll.isEmpty() && mtargets != null) {
|
||||
tcoll = Arrays.asList(mtargets);
|
||||
}
|
||||
|
||||
boolean handle = false;
|
||||
if (!scoll.isEmpty() && !tcoll.isEmpty()) {
|
||||
if (sourceState != null
|
||||
&& targetState != null
|
||||
&& StateMachineUtils.containsAtleastOne(scoll,
|
||||
StateMachineUtils.toStringCollection(sourceState.getIds()))
|
||||
&& StateMachineUtils.containsAtleastOne(tcoll,
|
||||
StateMachineUtils.toStringCollection(targetState.getIds()))) {
|
||||
handle = true;
|
||||
}
|
||||
} else if (!scoll.isEmpty()) {
|
||||
if (sourceState != null
|
||||
&& StateMachineUtils.containsAtleastOne(scoll,
|
||||
StateMachineUtils.toStringCollection(sourceState.getIds()))) {
|
||||
handle = true;
|
||||
}
|
||||
} else if (!tcoll.isEmpty()) {
|
||||
if (targetState != null
|
||||
&& StateMachineUtils.containsAtleastOne(tcoll,
|
||||
StateMachineUtils.toStringCollection(targetState.getIds()))) {
|
||||
handle = true;
|
||||
}
|
||||
} else if (scoll.isEmpty() && tcoll.isEmpty()) {
|
||||
handle = true;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
private List<Object> getStateMachineHandlerResults(List<StateMachineHandler<? extends Annotation, S, E>> stateMachineHandlers,
|
||||
final StateContext<S, E> stateContext) {
|
||||
StateMachineRuntime<S, E> runtime = new StateMachineRuntime<S, E>() {
|
||||
@Override
|
||||
public StateContext<S, E> getStateContext() {
|
||||
return stateContext;
|
||||
}
|
||||
};
|
||||
List<Object> results = new ArrayList<Object>();
|
||||
for (StateMachineHandler<? extends Annotation, S, E> handler : stateMachineHandlers) {
|
||||
results.add(handler.handle(runtime));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private class CacheEntry {
|
||||
final StateMachineHandler<? extends Annotation, S, E> handler;
|
||||
final Annotation annotation;
|
||||
final Annotation metaAnnotation;
|
||||
|
||||
public CacheEntry(StateMachineHandler<? extends Annotation, S, E> handler, Annotation annotation, Annotation metaAnnotation) {
|
||||
this.handler = handler;
|
||||
this.annotation = annotation;
|
||||
this.metaAnnotation = metaAnnotation;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
@@ -39,7 +40,9 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.annotation.EventHeaders;
|
||||
import org.springframework.statemachine.annotation.ExtendedStateVariable;
|
||||
import org.springframework.statemachine.support.AbstractExpressionEvaluator;
|
||||
import org.springframework.statemachine.support.AnnotatedMethodFilter;
|
||||
import org.springframework.statemachine.support.FixedMethodFilter;
|
||||
@@ -431,17 +434,24 @@ public class StateMachineMethodInvokerHelper<T, S, E> extends AbstractExpression
|
||||
|
||||
if (annotationType.equals(EventHeaders.class)) {
|
||||
sb.append("headers");
|
||||
} else if (annotationType.equals(ExtendedStateVariable.class)) {
|
||||
AnnotationAttributes annotationAttributes = AnnotationAttributes
|
||||
.fromMap(AnnotationUtils.getAnnotationAttributes(mappingAnnotation));
|
||||
String key = annotationAttributes.getAliasedString("value", ExtendedStateVariable.class, null);
|
||||
sb.append("variables.get('" + key + "')");
|
||||
}
|
||||
|
||||
} else if (ExtendedState.class.isAssignableFrom(parameterType)) {
|
||||
sb.append("extendedState");
|
||||
} else if (StateMachine.class.isAssignableFrom(parameterType)) {
|
||||
sb.append("stateMachine");
|
||||
}
|
||||
}
|
||||
if (hasUnqualifiedMapParameter) {
|
||||
if (targetParameterType != null && Map.class.isAssignableFrom(this.targetParameterType)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to determine payload matching parameter due to ambiguous Map typed parameters. "
|
||||
+ "Consider adding the @Payload and or @Headers annotations as appropriate.");
|
||||
+ "Consider adding the @EventHeaders and or @ExtendedStateVariable annotations as appropriate.");
|
||||
}
|
||||
}
|
||||
sb.append(")");
|
||||
@@ -466,6 +476,14 @@ public class StateMachineMethodInvokerHelper<T, S, E> extends AbstractExpression
|
||||
+ annotation.annotationType().getName() + "]");
|
||||
}
|
||||
match = annotation;
|
||||
} else if (type.equals(ExtendedStateVariable.class)) {
|
||||
if (match != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"At most one parameter annotation can be provided for message mapping, "
|
||||
+ "but found two: [" + match.annotationType().getName() + "] and ["
|
||||
+ annotation.annotationType().getName() + "]");
|
||||
}
|
||||
match = annotation;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
@@ -496,6 +514,14 @@ public class StateMachineMethodInvokerHelper<T, S, E> extends AbstractExpression
|
||||
return stateContext.getExtendedState();
|
||||
}
|
||||
|
||||
public Map<Object, Object> getVariables() {
|
||||
return getExtendedState().getVariables();
|
||||
}
|
||||
|
||||
public StateMachine<SS, EE> getStateMachine() {
|
||||
return stateContext.getStateMachine();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.processor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
|
||||
/**
|
||||
* Transition specific {@link StateMachineHandler}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class StateMachineOnTransitionHandler<S, E> extends StateMachineHandler<S, E> {
|
||||
|
||||
private final OnTransition metaAnnotation;
|
||||
private final Annotation annotation;
|
||||
|
||||
/**
|
||||
* Instantiates a new state machine on transition handler.
|
||||
*
|
||||
* @param beanClass the bean class
|
||||
* @param target the target
|
||||
* @param method the method
|
||||
* @param metaAnnotation the meta annotation
|
||||
* @param annotation the annotation
|
||||
*/
|
||||
public StateMachineOnTransitionHandler(Class<?> beanClass, Object target, Method method, OnTransition metaAnnotation, Annotation annotation) {
|
||||
super(beanClass, target, method);
|
||||
this.metaAnnotation = metaAnnotation;
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the meta annotation.
|
||||
*
|
||||
* @return the meta annotation
|
||||
*/
|
||||
public OnTransition getMetaAnnotation() {
|
||||
return metaAnnotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the annotation.
|
||||
*
|
||||
* @return the annotation
|
||||
*/
|
||||
public Annotation getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,15 +15,12 @@
|
||||
*/
|
||||
package org.springframework.statemachine.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -31,9 +28,6 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
@@ -45,12 +39,7 @@ import org.springframework.statemachine.StateMachineContext;
|
||||
import org.springframework.statemachine.access.StateMachineAccess;
|
||||
import org.springframework.statemachine.access.StateMachineAccessor;
|
||||
import org.springframework.statemachine.access.StateMachineFunction;
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
import org.springframework.statemachine.annotation.WithStateMachine;
|
||||
import org.springframework.statemachine.listener.StateMachineListener;
|
||||
import org.springframework.statemachine.processor.StateMachineHandler;
|
||||
import org.springframework.statemachine.processor.StateMachineOnTransitionHandler;
|
||||
import org.springframework.statemachine.processor.StateMachineRuntime;
|
||||
import org.springframework.statemachine.region.Region;
|
||||
import org.springframework.statemachine.state.AbstractState;
|
||||
import org.springframework.statemachine.state.ForkPseudoState;
|
||||
@@ -67,7 +56,6 @@ import org.springframework.statemachine.transition.TransitionKind;
|
||||
import org.springframework.statemachine.trigger.DefaultTriggerContext;
|
||||
import org.springframework.statemachine.trigger.Trigger;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -101,10 +89,6 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
|
||||
private volatile PseudoState<S, E> history;
|
||||
|
||||
private final Map<String, StateMachineOnTransitionHandler<S, E>> handlers = new HashMap<String, StateMachineOnTransitionHandler<S,E>>();
|
||||
|
||||
private volatile boolean handlersInitialized;
|
||||
|
||||
private final Map<Trigger<S, E>, Transition<S,E>> triggerToTransitionMap = new HashMap<Trigger<S,E>, Transition<S,E>>();
|
||||
|
||||
private final List<Transition<S, E>> triggerlessTransitions = new ArrayList<Transition<S,E>>();
|
||||
@@ -192,7 +176,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
public boolean sendEvent(Message<E> event) {
|
||||
if (hasStateMachineError()) {
|
||||
// TODO: should we throw exception?
|
||||
notifyEventNotAccepted(event);
|
||||
notifyEventNotAccepted(event, buildStateContext(null, null, getRelayStateMachine()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -200,18 +184,18 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
event = getStateMachineInterceptors().preEvent(event, this);
|
||||
} catch (Exception e) {
|
||||
log.info("Event " + event + " threw exception in interceptors, not accepting event");
|
||||
notifyEventNotAccepted(event);
|
||||
notifyEventNotAccepted(event, buildStateContext(null, null, getRelayStateMachine()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isComplete() || !isRunning()) {
|
||||
notifyEventNotAccepted(event);
|
||||
notifyEventNotAccepted(event, buildStateContext(null, null, getRelayStateMachine()));
|
||||
return false;
|
||||
}
|
||||
boolean accepted = acceptEvent(event);
|
||||
stateMachineExecutor.execute();
|
||||
if (!accepted) {
|
||||
notifyEventNotAccepted(event);
|
||||
notifyEventNotAccepted(event, buildStateContext(null, null, getRelayStateMachine()));
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
@@ -232,7 +216,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
extendedState.setExtendedStateChangeListener(new ExtendedStateChangeListener() {
|
||||
@Override
|
||||
public void changed(Object key, Object value) {
|
||||
notifyExtendedStateChanged(key, value);
|
||||
notifyExtendedStateChanged(key, value, buildStateContext(null, null, getRelayStateMachine()));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -277,16 +261,17 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
|
||||
@Override
|
||||
public void transit(Transition<S, E> t, StateContext<S, E> stateContext, Message<E> queuedMessage) {
|
||||
notifyTransitionStart(t);
|
||||
callHandlers(t.getSource(), t.getTarget(), queuedMessage);
|
||||
StateContext<S, E> stateContext2 = buildStateContext(queuedMessage, null, getRelayStateMachine());
|
||||
notifyTransitionStart(t, queuedMessage, buildStateContext(queuedMessage, null, getRelayStateMachine()));
|
||||
notifyTransition(t, queuedMessage, buildStateContext(queuedMessage, null, getRelayStateMachine()));
|
||||
if (t.getKind() == TransitionKind.INITIAL) {
|
||||
switchToState(t.getTarget(), queuedMessage, t, getRelayStateMachine());
|
||||
notifyStateMachineStarted(getRelayStateMachine());
|
||||
notifyStateMachineStarted(getRelayStateMachine(), stateContext2);
|
||||
} else if (t.getKind() != TransitionKind.INTERNAL) {
|
||||
switchToState(t.getTarget(), queuedMessage, t, getRelayStateMachine());
|
||||
}
|
||||
notifyTransition(t);
|
||||
notifyTransitionEnd(t);
|
||||
// TODO: looks like events should be called here and anno processing earlier
|
||||
notifyTransitionEnd(t, queuedMessage, buildStateContext(queuedMessage, null, getRelayStateMachine()));
|
||||
}
|
||||
});
|
||||
stateMachineExecutor = executor;
|
||||
@@ -307,6 +292,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
|
||||
@Override
|
||||
protected void doStart() {
|
||||
super.doStart();
|
||||
// if state is set assume nothing to do
|
||||
if (currentState != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -317,7 +303,8 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
// assume that state was set/reseted so we need to
|
||||
// dispatch started event which would net getting
|
||||
// dispatched via executor
|
||||
notifyStateMachineStarted(getRelayStateMachine());
|
||||
StateContext<S, E> stateContext = buildStateContext(null, null, getRelayStateMachine());
|
||||
notifyStateMachineStarted(getRelayStateMachine(), stateContext);
|
||||
return;
|
||||
}
|
||||
registerPseudoStateListener();
|
||||
@@ -338,7 +325,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
@Override
|
||||
protected void doStop() {
|
||||
stateMachineExecutor.stop();
|
||||
notifyStateMachineStopped(this);
|
||||
notifyStateMachineStopped(this, buildStateContext(null, null, this));
|
||||
currentState = null;
|
||||
initialEnabled = null;
|
||||
}
|
||||
@@ -359,7 +346,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
currentError = exception;
|
||||
}
|
||||
if (currentError != null) {
|
||||
notifyStateMachineError(this, currentError);
|
||||
notifyStateMachineError(this, currentError, buildStateContext(null, null, this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -697,6 +684,13 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
return new DefaultStateContext<S, E>(event, messageHeaders, extendedState, transition, stateMachine);
|
||||
}
|
||||
|
||||
private StateContext<S, E> buildStateContext(Message<E> message, Transition<S,E> transition, StateMachine<S, E> stateMachine, State<S, E> source, State<S, E> target) {
|
||||
E event = message != null ? message.getPayload() : null;
|
||||
MessageHeaders messageHeaders = message != null ? message.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
return new DefaultStateContext<S, E>(event, messageHeaders, extendedState, transition, stateMachine, source, target);
|
||||
}
|
||||
|
||||
private State<S, E> findDeepParent(State<S, E> state) {
|
||||
for (State<S, E> s : states) {
|
||||
if (s.getStates().contains(state)) {
|
||||
@@ -729,7 +723,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
start();
|
||||
}
|
||||
entryToState(state, message, transition, stateMachine);
|
||||
notifyStateChanged(notifyFrom, state);
|
||||
notifyStateChanged(notifyFrom, state, message, buildStateContext(message, null, getRelayStateMachine(), notifyFrom, state));
|
||||
nonDeepStatePresent = true;
|
||||
} else if (currentState == null && StateMachineUtils.isSubstate(findDeep, state)) {
|
||||
if (exit) {
|
||||
@@ -741,7 +735,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
start();
|
||||
}
|
||||
entryToState(findDeep, message, transition, stateMachine);
|
||||
notifyStateChanged(notifyFrom, findDeep);
|
||||
notifyStateChanged(notifyFrom, findDeep, message, buildStateContext(message, null, getRelayStateMachine(), notifyFrom, findDeep));
|
||||
}
|
||||
|
||||
if (currentState != null && !nonDeepStatePresent) {
|
||||
@@ -854,7 +848,8 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
|
||||
log.debug("Exit state=[" + state + "]");
|
||||
state.exit(stateContext);
|
||||
notifyStateExited(state);
|
||||
|
||||
notifyStateExited(state, message, buildStateContext(message, null, getRelayStateMachine(), state, null));
|
||||
}
|
||||
|
||||
private void entryToState(State<S, E> state, Message<E> message, Transition<S, E> transition, StateMachine<S, E> stateMachine) {
|
||||
@@ -882,115 +877,9 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
}
|
||||
}
|
||||
|
||||
notifyStateEntered(state);
|
||||
notifyStateEntered(state, message, buildStateContext(message, null, getRelayStateMachine(), null, state));
|
||||
log.debug("Enter state=[" + state + "]");
|
||||
state.entry(stateContext);
|
||||
}
|
||||
|
||||
private void callHandlers(State<S,E> sourceState, State<S,E> targetState, Message<E> message) {
|
||||
StateContext<S, E> stateContext = buildStateContext(message, null, getRelayStateMachine());
|
||||
getStateMachineHandlerResults(getStateMachineHandlers(sourceState, targetState), stateContext);
|
||||
}
|
||||
|
||||
private List<Object> getStateMachineHandlerResults(List<StateMachineHandler<S, E>> stateMachineHandlers,
|
||||
final StateContext<S, E> stateContext) {
|
||||
StateMachineRuntime<S, E> runtime = new StateMachineRuntime<S, E>() {
|
||||
@Override
|
||||
public StateContext<S, E> getStateContext() {
|
||||
return stateContext;
|
||||
}
|
||||
};
|
||||
List<Object> results = new ArrayList<Object>();
|
||||
for (StateMachineHandler<S, E> handler : stateMachineHandlers) {
|
||||
results.add(handler.handle(runtime));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private synchronized List<StateMachineHandler<S, E>> getStateMachineHandlers(State<S, E> sourceState,
|
||||
State<S, E> targetState) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
|
||||
// TODO think how to handle null bf
|
||||
if (beanFactory == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Assert.state(beanFactory instanceof ListableBeanFactory, "Bean factory must be instance of ListableBeanFactory");
|
||||
|
||||
if (!handlersInitialized) {
|
||||
Map<String, StateMachineOnTransitionHandler> handlersMap = ((ListableBeanFactory) beanFactory)
|
||||
.getBeansOfType(StateMachineOnTransitionHandler.class);
|
||||
for (Entry<String, StateMachineOnTransitionHandler> entry : handlersMap.entrySet()) {
|
||||
handlers.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
handlersInitialized = true;
|
||||
}
|
||||
|
||||
List<StateMachineHandler<S, E>> handlersList = new ArrayList<StateMachineHandler<S, E>>();
|
||||
|
||||
for (Entry<String, StateMachineOnTransitionHandler<S, E>> entry : handlers.entrySet()) {
|
||||
// add only matching names from beanName and WithStateMachine name field
|
||||
WithStateMachine withStateMachine = AnnotationUtils.findAnnotation(entry.getValue().getBeanClass(), WithStateMachine.class);
|
||||
if (withStateMachine == null || !ObjectUtils.nullSafeEquals(withStateMachine.name(), getBeanName())) {
|
||||
continue;
|
||||
}
|
||||
OnTransition metaAnnotation = entry.getValue().getMetaAnnotation();
|
||||
Annotation annotation = entry.getValue().getAnnotation();
|
||||
if (transitionHandlerMatch(metaAnnotation, annotation, sourceState, targetState)) {
|
||||
handlersList.add(entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
OrderComparator comparator = new OrderComparator();
|
||||
Collections.sort(handlersList, comparator);
|
||||
return handlersList;
|
||||
}
|
||||
|
||||
private boolean transitionHandlerMatch(OnTransition metaAnnotation, Annotation annotation, State<S, E> sourceState, State<S, E> targetState) {
|
||||
String[] msources = metaAnnotation.source();
|
||||
String[] mtargets = metaAnnotation.target();
|
||||
|
||||
Map<String, Object> annotationAttributes = AnnotationUtils.getAnnotationAttributes(annotation);
|
||||
Object source = annotationAttributes.get("source");
|
||||
Object target = annotationAttributes.get("target");
|
||||
|
||||
Collection<String> scoll = StateMachineUtils.toStringCollection(source);
|
||||
if (scoll.isEmpty()) {
|
||||
scoll = Arrays.asList(msources);
|
||||
}
|
||||
Collection<String> tcoll = StateMachineUtils.toStringCollection(target);
|
||||
if (tcoll.isEmpty()) {
|
||||
tcoll = Arrays.asList(mtargets);
|
||||
}
|
||||
|
||||
boolean handle = false;
|
||||
if (!scoll.isEmpty() && !tcoll.isEmpty()) {
|
||||
if (sourceState != null
|
||||
&& targetState != null
|
||||
&& StateMachineUtils.containsAtleastOne(scoll,
|
||||
StateMachineUtils.toStringCollection(sourceState.getIds()))
|
||||
&& StateMachineUtils.containsAtleastOne(tcoll,
|
||||
StateMachineUtils.toStringCollection(targetState.getIds()))) {
|
||||
handle = true;
|
||||
}
|
||||
} else if (!scoll.isEmpty()) {
|
||||
if (sourceState != null
|
||||
&& StateMachineUtils.containsAtleastOne(scoll,
|
||||
StateMachineUtils.toStringCollection(sourceState.getIds()))) {
|
||||
handle = true;
|
||||
}
|
||||
} else if (!tcoll.isEmpty()) {
|
||||
if (targetState != null
|
||||
&& StateMachineUtils.containsAtleastOne(tcoll,
|
||||
StateMachineUtils.toStringCollection(targetState.getIds()))) {
|
||||
handle = true;
|
||||
}
|
||||
} else if (scoll.isEmpty() && tcoll.isEmpty()) {
|
||||
handle = true;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,37 +15,48 @@
|
||||
*/
|
||||
package org.springframework.statemachine.support;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
public class DefaultStateContext<S, E> implements StateContext<S, E> {
|
||||
|
||||
private final E event;
|
||||
|
||||
private final MessageHeaders messageHeaders;
|
||||
|
||||
private final ExtendedState extendedState;
|
||||
|
||||
private final Transition<S,E> transition;
|
||||
|
||||
private final StateMachine<S, E> stateMachine;
|
||||
private final State<S, E> source;
|
||||
private final State<S, E> target;
|
||||
|
||||
public DefaultStateContext(E event, MessageHeaders messageHeaders, ExtendedState extendedState, Transition<S,E> transition, StateMachine<S, E> stateMachine) {
|
||||
this(event, messageHeaders, extendedState, transition, stateMachine, null, null);
|
||||
}
|
||||
|
||||
public DefaultStateContext(E event, MessageHeaders messageHeaders, ExtendedState extendedState, Transition<S,E> transition, StateMachine<S, E> stateMachine, State<S, E> source, State<S, E> target) {
|
||||
this.event = event;
|
||||
this.messageHeaders = messageHeaders;
|
||||
this.extendedState = extendedState;
|
||||
this.transition = transition;
|
||||
this.stateMachine = stateMachine;
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public E getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<E> getMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageHeaders getMessageHeaders() {
|
||||
return messageHeaders;
|
||||
@@ -76,4 +87,18 @@ public class DefaultStateContext<S, E> implements StateContext<S, E> {
|
||||
return stateMachine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State<S, E> getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State<S, E> getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exception getException() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,12 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.event.StateMachineEventPublisher;
|
||||
import org.springframework.statemachine.listener.CompositeStateMachineListener;
|
||||
import org.springframework.statemachine.listener.StateMachineListener;
|
||||
import org.springframework.statemachine.processor.StateMachineHandlerCallHelper;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -51,10 +53,25 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
/** Flag for application context events */
|
||||
private boolean contextEventsEnabled = true;
|
||||
|
||||
private final StateMachineInterceptorList<S, E> interceptors =
|
||||
new StateMachineInterceptorList<S, E>();
|
||||
|
||||
private final StateMachineInterceptorList<S, E> interceptors = new StateMachineInterceptorList<S, E>();
|
||||
private String beanName;
|
||||
private volatile boolean handlersInitialized;
|
||||
private final StateMachineHandlerCallHelper<S, E> stateMachineHandlerCallHelper = new StateMachineHandlerCallHelper<S, E>();
|
||||
|
||||
@Override
|
||||
protected void doStart() {
|
||||
super.doStart();
|
||||
if (!handlersInitialized) {
|
||||
try {
|
||||
stateMachineHandlerCallHelper.setBeanFactory(getBeanFactory());
|
||||
stateMachineHandlerCallHelper.afterPropertiesSet();
|
||||
} catch (Exception e) {
|
||||
log.error("Unable to initialize annotation handlers", e);
|
||||
} finally {
|
||||
handlersInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanName(String name) {
|
||||
@@ -111,7 +128,8 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
return stateListener;
|
||||
}
|
||||
|
||||
protected void notifyStateChanged(State<S,E> source, State<S,E> target) {
|
||||
protected void notifyStateChanged(State<S,E> source, State<S,E> target, Message<E> message, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnStateChanged(getBeanName(), null, message, stateContext);
|
||||
stateListener.stateChanged(source, target);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
@@ -121,7 +139,8 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyStateEntered(State<S,E> state) {
|
||||
protected void notifyStateEntered(State<S,E> state, Message<E> message, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnStateEntry(getBeanName(), null, message, stateContext);
|
||||
stateListener.stateEntered(state);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
@@ -131,7 +150,8 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyStateExited(State<S,E> state) {
|
||||
protected void notifyStateExited(State<S,E> state, Message<E> message, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnStateExit(getBeanName(), null, message, stateContext);
|
||||
stateListener.stateExited(state);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
@@ -141,7 +161,8 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyEventNotAccepted(Message<E> event) {
|
||||
protected void notifyEventNotAccepted(Message<E> event, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnEventNotAccepted(getBeanName(), stateContext);
|
||||
stateListener.eventNotAccepted(event);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
@@ -151,7 +172,8 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyTransitionStart(Transition<S,E> transition) {
|
||||
protected void notifyTransitionStart(Transition<S,E> transition, Message<E> message, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnTransitionStart(getBeanName(), transition, message, stateContext);
|
||||
stateListener.transitionStarted(transition);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
@@ -161,7 +183,8 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyTransition(Transition<S,E> transition) {
|
||||
protected void notifyTransition(Transition<S,E> transition, Message<E> message, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnTransition(getBeanName(), transition, message, stateContext);
|
||||
stateListener.transition(transition);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
@@ -171,7 +194,8 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyTransitionEnd(Transition<S,E> transition) {
|
||||
protected void notifyTransitionEnd(Transition<S,E> transition, Message<E> message, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnTransitionEnd(getBeanName(), transition, message, stateContext);
|
||||
stateListener.transitionEnded(transition);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
@@ -181,7 +205,8 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyStateMachineStarted(StateMachine<S, E> stateMachine) {
|
||||
protected void notifyStateMachineStarted(StateMachine<S, E> stateMachine, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnStateMachineStart(getBeanName(), stateContext);
|
||||
stateListener.stateMachineStarted(stateMachine);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
@@ -191,7 +216,8 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyStateMachineStopped(StateMachine<S, E> stateMachine) {
|
||||
protected void notifyStateMachineStopped(StateMachine<S, E> stateMachine, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnStateMachineStop(getBeanName(), stateContext);
|
||||
stateListener.stateMachineStopped(stateMachine);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
@@ -201,7 +227,8 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyStateMachineError(StateMachine<S, E> stateMachine, Exception exception) {
|
||||
protected void notifyStateMachineError(StateMachine<S, E> stateMachine, Exception exception, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnStateMachineError(getBeanName(), stateContext);
|
||||
stateListener.stateMachineError(stateMachine, exception);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
@@ -211,7 +238,8 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyExtendedStateChanged(Object key, Object value) {
|
||||
protected void notifyExtendedStateChanged(Object key, Object value, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnExtendedStateChanged(getBeanName(), key, value, stateContext);
|
||||
stateListener.extendedStateChanged(key, value);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
|
||||
@@ -24,6 +24,8 @@ import java.util.Collection;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
@@ -75,8 +77,10 @@ public class EnumStateMachineTests extends AbstractStateMachineTests {
|
||||
transitions.add(transitionFromS2ToS3);
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
BeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
ObjectStateMachine<TestStates, TestEvents> machine = new ObjectStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.setBeanFactory(beanFactory);
|
||||
machine.afterPropertiesSet();
|
||||
machine.start();
|
||||
|
||||
@@ -148,8 +152,10 @@ public class EnumStateMachineTests extends AbstractStateMachineTests {
|
||||
|
||||
// create machine
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
BeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
ObjectStateMachine<TestStates, TestEvents> machine = new ObjectStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.setBeanFactory(beanFactory);
|
||||
machine.afterPropertiesSet();
|
||||
machine.start();
|
||||
|
||||
@@ -189,8 +195,10 @@ public class EnumStateMachineTests extends AbstractStateMachineTests {
|
||||
transitions.add(transitionInternalSI);
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
BeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
ObjectStateMachine<TestStates, TestEvents> machine = new ObjectStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.setBeanFactory(beanFactory);
|
||||
machine.afterPropertiesSet();
|
||||
machine.start();
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ import java.util.Collection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -102,9 +104,11 @@ public class RegionMachineTests extends AbstractStateMachineTests {
|
||||
transitions.add(transitionFromS2ToS3);
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
BeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
Transition<TestStates,TestEvents> initialTransition = new InitialTransition<TestStates,TestEvents>(stateSI);
|
||||
ObjectStateMachine<TestStates, TestEvents> machine = new ObjectStateMachine<TestStates, TestEvents>(states, transitions, stateSI, initialTransition, null, null);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.setBeanFactory(beanFactory);
|
||||
machine.afterPropertiesSet();
|
||||
machine.start();
|
||||
|
||||
@@ -131,6 +135,7 @@ public class RegionMachineTests extends AbstractStateMachineTests {
|
||||
@Test
|
||||
public void testMultiRegionBuildRaw() throws Exception {
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
BeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
PseudoState<TestStates,TestEvents> pseudoState = new DefaultPseudoState<TestStates,TestEvents>(PseudoStateKind.INITIAL);
|
||||
State<TestStates,TestEvents> stateSI = new EnumState<TestStates,TestEvents>(TestStates.SI, pseudoState);
|
||||
|
||||
@@ -169,6 +174,7 @@ public class RegionMachineTests extends AbstractStateMachineTests {
|
||||
Transition<TestStates,TestEvents> initialTransition11 = new InitialTransition<TestStates,TestEvents>(stateS111);
|
||||
ObjectStateMachine<TestStates, TestEvents> machine11 = new ObjectStateMachine<TestStates, TestEvents>(states11, transitions11, stateS111, initialTransition11, null, null);
|
||||
machine11.setTaskExecutor(taskExecutor);
|
||||
machine11.setBeanFactory(beanFactory);
|
||||
machine11.afterPropertiesSet();
|
||||
|
||||
Collection<State<TestStates,TestEvents>> states12 = new ArrayList<State<TestStates,TestEvents>>();
|
||||
@@ -181,6 +187,7 @@ public class RegionMachineTests extends AbstractStateMachineTests {
|
||||
Transition<TestStates,TestEvents> initialTransition12 = new InitialTransition<TestStates,TestEvents>(stateS121);
|
||||
ObjectStateMachine<TestStates, TestEvents> machine12 = new ObjectStateMachine<TestStates, TestEvents>(states12, transitions12, stateS121, initialTransition12, null, null);
|
||||
machine12.setTaskExecutor(taskExecutor);
|
||||
machine12.setBeanFactory(beanFactory);
|
||||
machine12.afterPropertiesSet();
|
||||
|
||||
Collection<Region<TestStates,TestEvents>> regions = new ArrayList<Region<TestStates,TestEvents>>();
|
||||
@@ -198,6 +205,7 @@ public class RegionMachineTests extends AbstractStateMachineTests {
|
||||
ObjectStateMachine<TestStates, TestEvents> machine = new ObjectStateMachine<TestStates, TestEvents>(states, transitions, stateR, initialTransition, null, null);
|
||||
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.setBeanFactory(beanFactory);
|
||||
machine.afterPropertiesSet();
|
||||
machine.start();
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ import java.util.Collection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -128,11 +130,15 @@ public class SubStateMachineTests extends AbstractStateMachineTests {
|
||||
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
BeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.setBeanFactory(beanFactory);
|
||||
machine.afterPropertiesSet();
|
||||
submachine1.setTaskExecutor(taskExecutor);
|
||||
submachine1.setBeanFactory(beanFactory);
|
||||
submachine1.afterPropertiesSet();
|
||||
submachine11.setTaskExecutor(taskExecutor);
|
||||
submachine11.setBeanFactory(beanFactory);
|
||||
submachine11.afterPropertiesSet();
|
||||
machine.start();
|
||||
|
||||
@@ -220,9 +226,12 @@ public class SubStateMachineTests extends AbstractStateMachineTests {
|
||||
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
BeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.setBeanFactory(beanFactory);
|
||||
machine.afterPropertiesSet();
|
||||
submachine11.setTaskExecutor(taskExecutor);
|
||||
submachine11.setBeanFactory(beanFactory);
|
||||
submachine11.afterPropertiesSet();
|
||||
machine.start();
|
||||
|
||||
@@ -318,11 +327,15 @@ public class SubStateMachineTests extends AbstractStateMachineTests {
|
||||
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
BeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.setBeanFactory(beanFactory);
|
||||
machine.afterPropertiesSet();
|
||||
submachine1.setTaskExecutor(taskExecutor);
|
||||
submachine1.setBeanFactory(beanFactory);
|
||||
submachine1.afterPropertiesSet();
|
||||
submachine11.setTaskExecutor(taskExecutor);
|
||||
submachine11.setBeanFactory(beanFactory);
|
||||
submachine11.afterPropertiesSet();
|
||||
machine.start();
|
||||
|
||||
|
||||
@@ -32,7 +32,10 @@ import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.statemachine.AbstractStateMachineTests;
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.ObjectStateMachine;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.StateMachineSystemConstants;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
@@ -40,39 +43,124 @@ import org.springframework.statemachine.config.builders.StateMachineTransitionCo
|
||||
|
||||
public class MethodAnnotationTests extends AbstractStateMachineTests {
|
||||
|
||||
@Override
|
||||
protected AnnotationConfigApplicationContext buildContext() {
|
||||
return new AnnotationConfigApplicationContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testMethodAnnotations() throws Exception {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BaseConfig.class, BeanConfig1.class, Config1.class);
|
||||
public void testOnTransition() throws Exception {
|
||||
context.register(BaseConfig.class, BeanConfig1.class, Config1.class);
|
||||
context.refresh();
|
||||
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
assertThat(context.containsBean("fooMachine"), is(true));
|
||||
Bean1 bean1 = context.getBean(Bean1.class);
|
||||
|
||||
bean1.reset(1, 1, 1, 1, 1, 1, 1, 1);
|
||||
machine.start();
|
||||
|
||||
assertThat(bean1.onMethod1Latch.await(2, TimeUnit.SECONDS), is(false));
|
||||
assertThat(bean1.onOnTransitionLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean1.onMethod1Count, is(0));
|
||||
assertThat(bean1.onOnTransitionCount, is(1));
|
||||
assertThat(bean1.onTransitionFromS1ToS2Latch.await(1, TimeUnit.SECONDS), is(false));
|
||||
assertThat(bean1.onTransitionLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean1.onTransitionFromS1ToS2Count, is(0));
|
||||
assertThat(bean1.onTransitionCount, is(1));
|
||||
|
||||
bean1.reset(1, 1);
|
||||
|
||||
// this event should cause 'method1' to get called
|
||||
bean1.reset(1, 1, 1, 1, 1, 1, 1, 1);
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
|
||||
|
||||
assertThat(bean1.onMethod1Latch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean1.onOnTransitionLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean1.onMethod1Count, is(1));
|
||||
assertThat(bean1.onOnTransitionCount, is(1));
|
||||
assertThat(bean1.onTransitionFromS1ToS2Latch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean1.onTransitionLatch.await(1, TimeUnit.SECONDS), is(true));
|
||||
|
||||
context.close();
|
||||
assertThat(bean1.onTransitionFromS1ToS2Count, is(1));
|
||||
assertThat(bean1.onTransitionCount, is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOnStateChanged() throws Exception {
|
||||
context.register(BaseConfig.class, BeanConfig1.class, Config1.class);
|
||||
context.refresh();
|
||||
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
assertThat(context.containsBean("fooMachine"), is(true));
|
||||
Bean1 bean1 = context.getBean(Bean1.class);
|
||||
|
||||
bean1.reset(1, 1, 1, 1, 1, 1, 1, 1);
|
||||
machine.start();
|
||||
|
||||
assertThat(bean1.onStateChangedFromS1ToS2Latch.await(1, TimeUnit.SECONDS), is(false));
|
||||
assertThat(bean1.onStateChangedLatch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean1.onStateChangedFromS1ToS2Count, is(0));
|
||||
assertThat(bean1.onStateChangedCount, is(1));
|
||||
|
||||
bean1.reset(1, 1, 1, 1, 1, 1, 1, 1);
|
||||
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
|
||||
|
||||
assertThat(bean1.onStateChangedFromS1ToS2Latch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean1.onStateChangedLatch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean1.onStateChangedFromS1ToS2Count, is(1));
|
||||
assertThat(bean1.onStateChangedCount, is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOnStateMachineStartStop() throws Exception {
|
||||
context.register(BaseConfig.class, BeanConfig1.class, Config1.class);
|
||||
context.refresh();
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
assertThat(context.containsBean("fooMachine"), is(true));
|
||||
Bean1 bean1 = context.getBean(Bean1.class);
|
||||
|
||||
bean1.reset(1, 1, 1, 1, 1, 1, 1, 1);
|
||||
|
||||
machine.start();
|
||||
assertThat(bean1.onStateMachineStartLatch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean1.onStateMachineStartCount, is(1));
|
||||
assertThat(bean1.onStateMachineStopLatch.await(1, TimeUnit.SECONDS), is(false));
|
||||
assertThat(bean1.onStateMachineStopCount, is(0));
|
||||
|
||||
bean1.reset(1, 1, 1, 1, 1, 1, 1, 1);
|
||||
machine.stop();
|
||||
assertThat(bean1.onStateMachineStartLatch.await(1, TimeUnit.SECONDS), is(false));
|
||||
assertThat(bean1.onStateMachineStartCount, is(0));
|
||||
assertThat(bean1.onStateMachineStopLatch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean1.onStateMachineStopCount, is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOnExtendedStateChanged() throws Exception {
|
||||
context.register(BaseConfig.class, BeanConfig5.class, Config1.class);
|
||||
context.refresh();
|
||||
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
assertThat(context.containsBean("fooMachine"), is(true));
|
||||
Bean5 bean5 = context.getBean(Bean5.class);
|
||||
machine.start();
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).setHeader("V1", "V1val").build());
|
||||
|
||||
assertThat(bean5.onExtendedStateChanged1Latch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean5.onExtendedStateChanged1Count, is(1));
|
||||
|
||||
assertThat(bean5.onExtendedStateChanged2Latch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean5.onExtendedStateChanged2Count, is(1));
|
||||
assertThat(bean5.onExtendedStateChanged2Value, is("V1val"));
|
||||
|
||||
assertThat(bean5.onExtendedStateChangedKeyV2Latch.await(1, TimeUnit.SECONDS), is(false));
|
||||
assertThat(bean5.onExtendedStateChangedKeyV2Count, is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testMethodAnnotations2() throws Exception {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BaseConfig.class, BeanConfig2.class, Config1.class);
|
||||
context.register(BaseConfig.class, BeanConfig2.class, Config1.class);
|
||||
context.refresh();
|
||||
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
@@ -93,35 +181,161 @@ public class MethodAnnotationTests extends AbstractStateMachineTests {
|
||||
assertThat(bean2.onMethod2Latch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean2.variable, notNullValue());
|
||||
assertThat((String)bean2.variable, is("jee"));
|
||||
}
|
||||
|
||||
context.close();
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testMethodAnnotations3() throws Exception {
|
||||
context.register(BaseConfig.class, BeanConfig3.class, Config1.class);
|
||||
context.refresh();
|
||||
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
assertThat(context.containsBean("fooMachine"), is(true));
|
||||
machine.start();
|
||||
|
||||
Bean3 bean3 = context.getBean(Bean3.class);
|
||||
|
||||
// this event should cause 'method1' to get called
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
|
||||
|
||||
assertThat(bean3.onStateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testMethodAnnotations4() throws Exception {
|
||||
context.register(BaseConfig.class, BeanConfig4.class, Config1.class);
|
||||
context.refresh();
|
||||
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
assertThat(context.containsBean("fooMachine"), is(true));
|
||||
Bean4 bean4 = context.getBean(Bean4.class);
|
||||
machine.start();
|
||||
|
||||
assertThat(bean4.onStateEntryLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean4.onStateExitLatch.await(2, TimeUnit.SECONDS), is(false));
|
||||
assertThat(bean4.onStateEntryCount, is(1));
|
||||
assertThat(bean4.onStateExitCount, is(0));
|
||||
|
||||
bean4.reset(1, 1);
|
||||
|
||||
// this event should cause 'method1' to get called
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
|
||||
assertThat(bean4.onStateEntryLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean4.onStateExitLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean4.onStateEntryCount, is(1));
|
||||
assertThat(bean4.onStateExitCount, is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testMethodAnnotations5() throws Exception {
|
||||
context.register(BaseConfig.class, BeanConfig6.class, Config1.class);
|
||||
context.refresh();
|
||||
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
Bean6 bean6 = context.getBean(Bean6.class);
|
||||
machine.start();
|
||||
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E4).build());
|
||||
assertThat(bean6.onEventNotAcceptedLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
}
|
||||
|
||||
@WithStateMachine
|
||||
static class Bean1 {
|
||||
|
||||
CountDownLatch onMethod1Latch = new CountDownLatch(1);
|
||||
CountDownLatch onOnTransitionLatch = new CountDownLatch(1);
|
||||
int onMethod1Count;
|
||||
int onOnTransitionCount;
|
||||
CountDownLatch onTransitionFromS1ToS2Latch = new CountDownLatch(1);
|
||||
CountDownLatch onTransitionLatch = new CountDownLatch(1);
|
||||
CountDownLatch onStateChangedFromS1ToS2Latch = new CountDownLatch(1);
|
||||
CountDownLatch onStateChangedLatch = new CountDownLatch(1);
|
||||
CountDownLatch onTransitionEndLatch = new CountDownLatch(1);
|
||||
CountDownLatch onTransitionStartLatch = new CountDownLatch(1);
|
||||
CountDownLatch onStateMachineStartLatch = new CountDownLatch(1);
|
||||
CountDownLatch onStateMachineStopLatch = new CountDownLatch(1);
|
||||
int onTransitionFromS1ToS2Count = 0;
|
||||
int onTransitionCount = 0;
|
||||
int onStateChangedFromS1ToS2Count = 0;
|
||||
int onStateChangedCount = 0;
|
||||
int onTransitionEndCount = 0;
|
||||
int onTransitionStartCount = 0;
|
||||
int onStateMachineStartCount = 0;
|
||||
int onStateMachineStopCount = 0;
|
||||
|
||||
@OnTransition(source = "S1", target = "S2")
|
||||
public void method1() {
|
||||
onMethod1Count++;
|
||||
onMethod1Latch.countDown();
|
||||
public void onTransitionFromS1ToS2() {
|
||||
onTransitionFromS1ToS2Count++;
|
||||
onTransitionFromS1ToS2Latch.countDown();
|
||||
}
|
||||
|
||||
@OnTransition
|
||||
public void onTransition() {
|
||||
onOnTransitionCount++;
|
||||
onOnTransitionLatch.countDown();
|
||||
onTransitionCount++;
|
||||
onTransitionLatch.countDown();
|
||||
}
|
||||
|
||||
public void reset(int a1, int a2) {
|
||||
onMethod1Latch = new CountDownLatch(a1);
|
||||
onOnTransitionLatch = new CountDownLatch(a2);
|
||||
onMethod1Count = 0;
|
||||
onOnTransitionCount = 0;
|
||||
@OnTransitionEnd
|
||||
public void onTransitionEnd() {
|
||||
onTransitionEndCount++;
|
||||
onTransitionEndLatch.countDown();
|
||||
}
|
||||
|
||||
@OnTransitionStart
|
||||
public void onTransitionStart() {
|
||||
onTransitionStartCount++;
|
||||
onTransitionStartLatch.countDown();
|
||||
}
|
||||
|
||||
@OnStateChanged(source = "S1", target = "S2")
|
||||
public void onStateChangedFromS1ToS2() {
|
||||
onStateChangedFromS1ToS2Count++;
|
||||
onStateChangedFromS1ToS2Latch.countDown();
|
||||
}
|
||||
|
||||
@OnStateChanged
|
||||
public void onStateChanged() {
|
||||
onStateChangedCount++;
|
||||
onStateChangedLatch.countDown();
|
||||
}
|
||||
|
||||
@OnStateMachineStart
|
||||
public void onStateMachineStart() {
|
||||
onStateMachineStartLatch.countDown();
|
||||
onStateMachineStartCount++;
|
||||
}
|
||||
|
||||
@OnStateMachineStart
|
||||
public void onStateMachineStartWithParam(StateMachine<TestStates,TestEvents> machine) {
|
||||
}
|
||||
|
||||
@OnStateMachineStop
|
||||
public void onStateMachineStop() {
|
||||
onStateMachineStopLatch.countDown();
|
||||
onStateMachineStopCount++;
|
||||
}
|
||||
|
||||
@OnStateMachineStop
|
||||
public void onStateMachineStopWithParam(StateMachine<TestStates,TestEvents> machine) {
|
||||
}
|
||||
|
||||
public void reset(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
|
||||
onTransitionFromS1ToS2Latch = new CountDownLatch(a1);
|
||||
onTransitionLatch = new CountDownLatch(a2);
|
||||
onStateChangedFromS1ToS2Latch = new CountDownLatch(a3);
|
||||
onStateChangedLatch = new CountDownLatch(a4);
|
||||
onTransitionEndLatch = new CountDownLatch(a5);
|
||||
onTransitionStartLatch = new CountDownLatch(a6);
|
||||
onStateMachineStartLatch = new CountDownLatch(a7);
|
||||
onStateMachineStopLatch = new CountDownLatch(a8);
|
||||
onTransitionFromS1ToS2Count = 0;
|
||||
onTransitionCount = 0;
|
||||
onStateChangedFromS1ToS2Count = 0;
|
||||
onStateChangedCount = 0;
|
||||
onTransitionEndCount = 0;
|
||||
onTransitionStartCount = 0;
|
||||
onStateMachineStartCount = 0;
|
||||
onStateMachineStopCount = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -151,6 +365,100 @@ public class MethodAnnotationTests extends AbstractStateMachineTests {
|
||||
|
||||
}
|
||||
|
||||
@WithStateMachine
|
||||
static class Bean3 {
|
||||
|
||||
CountDownLatch onStateChangedLatch = new CountDownLatch(1);
|
||||
|
||||
@OnStateChanged
|
||||
public void onStateChanged() {
|
||||
onStateChangedLatch.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@WithStateMachine
|
||||
static class Bean4 {
|
||||
|
||||
CountDownLatch onStateEntryLatch = new CountDownLatch(1);
|
||||
CountDownLatch onStateExitLatch = new CountDownLatch(1);
|
||||
int onStateEntryCount = 0;
|
||||
int onStateExitCount = 0;
|
||||
|
||||
@OnStateEntry
|
||||
public void onStateEntry() {
|
||||
onStateEntryCount++;
|
||||
onStateEntryLatch.countDown();
|
||||
}
|
||||
|
||||
@OnStateExit
|
||||
public void onStateExit() {
|
||||
onStateExitCount++;
|
||||
onStateExitLatch.countDown();
|
||||
}
|
||||
|
||||
public void reset(int a1, int a2) {
|
||||
onStateEntryCount = 0;
|
||||
onStateExitCount = 0;
|
||||
onStateEntryLatch = new CountDownLatch(a1);
|
||||
onStateExitLatch = new CountDownLatch(a2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@WithStateMachine
|
||||
static class Bean5 {
|
||||
|
||||
CountDownLatch onExtendedStateChanged1Latch = new CountDownLatch(1);
|
||||
CountDownLatch onExtendedStateChanged2Latch = new CountDownLatch(1);
|
||||
CountDownLatch onExtendedStateChangedKeyV2Latch = new CountDownLatch(1);
|
||||
int onExtendedStateChanged1Count = 0;
|
||||
int onExtendedStateChanged2Count = 0;
|
||||
Object onExtendedStateChanged2Value = null;
|
||||
int onExtendedStateChangedKeyV2Count = 0;
|
||||
|
||||
@OnExtendedStateChanged
|
||||
public void onExtendedStateChanged1() {
|
||||
onExtendedStateChanged1Count++;
|
||||
onExtendedStateChanged1Latch.countDown();
|
||||
}
|
||||
|
||||
@OnExtendedStateChanged
|
||||
public void onExtendedStateChanged2(@ExtendedStateVariable("V1") Object value) {
|
||||
onExtendedStateChanged2Value = value;
|
||||
onExtendedStateChanged2Count++;
|
||||
onExtendedStateChanged2Latch.countDown();
|
||||
}
|
||||
|
||||
@OnExtendedStateChanged(key = "V2")
|
||||
public void onExtendedStateChangedKeyV2() {
|
||||
onExtendedStateChangedKeyV2Count++;
|
||||
onExtendedStateChangedKeyV2Latch.countDown();
|
||||
}
|
||||
|
||||
public void reset(int a1, int a2, int a3) {
|
||||
onExtendedStateChanged1Latch = new CountDownLatch(a1);
|
||||
onExtendedStateChanged2Latch = new CountDownLatch(a2);
|
||||
onExtendedStateChangedKeyV2Latch = new CountDownLatch(a3);
|
||||
onExtendedStateChanged1Count = 0;
|
||||
onExtendedStateChanged2Count = 0;
|
||||
onExtendedStateChanged2Value = null;
|
||||
onExtendedStateChangedKeyV2Count = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@WithStateMachine
|
||||
static class Bean6 {
|
||||
CountDownLatch onEventNotAcceptedLatch = new CountDownLatch(1);
|
||||
|
||||
@OnEventNotAccepted
|
||||
public void onEventNotAcceptedLatch() {
|
||||
onEventNotAcceptedLatch.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class BeanConfig1 {
|
||||
|
||||
@@ -171,6 +479,46 @@ public class MethodAnnotationTests extends AbstractStateMachineTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class BeanConfig3 {
|
||||
|
||||
@Bean
|
||||
public Bean3 bean3() {
|
||||
return new Bean3();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class BeanConfig4 {
|
||||
|
||||
@Bean
|
||||
public Bean4 bean4() {
|
||||
return new Bean4();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class BeanConfig5 {
|
||||
|
||||
@Bean
|
||||
public Bean5 bean5() {
|
||||
return new Bean5();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class BeanConfig6 {
|
||||
|
||||
@Bean
|
||||
public Bean6 bean6() {
|
||||
return new Bean6();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine(name = {StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, "fooMachine"})
|
||||
static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
@@ -192,6 +540,7 @@ public class MethodAnnotationTests extends AbstractStateMachineTests {
|
||||
.event(TestEvents.E1)
|
||||
.guard(testGuard())
|
||||
.action(testAction())
|
||||
.action(extendedStateAction())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S2)
|
||||
@@ -214,6 +563,20 @@ public class MethodAnnotationTests extends AbstractStateMachineTests {
|
||||
return new TestAction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Action<TestStates, TestEvents> extendedStateAction() {
|
||||
return new Action<TestStates, TestEvents>() {
|
||||
|
||||
@Override
|
||||
public void execute(StateContext<TestStates, TestEvents> context) {
|
||||
String e1 = context.getMessageHeaders().get("V1", String.class);
|
||||
if (e1 != null) {
|
||||
context.getExtendedState().getVariables().put("V1", e1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -363,7 +363,6 @@ public class AnnotatedMethodTests extends AbstractStateMachineTests {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class Config4 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@@ -23,6 +23,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.statemachine.AbstractStateMachineTests;
|
||||
import org.springframework.statemachine.ObjectStateMachine;
|
||||
@@ -69,8 +71,10 @@ public class RegionStateTests extends AbstractStateMachineTests {
|
||||
transitions.add(transitionFromS2ToS3);
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
BeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
ObjectStateMachine<TestStates, TestEvents> machine = new ObjectStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.setBeanFactory(beanFactory);
|
||||
machine.afterPropertiesSet();
|
||||
machine.start();
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
@@ -83,8 +85,10 @@ public class SubmachineStateTests extends AbstractStateMachineTests {
|
||||
transitions.add(transitionFromS2ToS3);
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
BeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
ObjectStateMachine<TestStates, TestEvents> machine = new ObjectStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.setBeanFactory(beanFactory);
|
||||
machine.afterPropertiesSet();
|
||||
machine.start();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user