spring-statemachine 2.3.1 - How to get a StateMachine from a StateMachineModelFactory? - spring-statemachine

I'm having the following business case while working with spring-statemachine 2.3.1 in a project:
The state machine is defined with the Papyrus plugin and loaded from an uml file using the UmlStateMachineModelFactory as shown below:
public class MyStateMachineConfig extends StateMachineConfigurerAdapter<String, String>
{
#Override
public void configure(StateMachineModelConfigurer<String, String> model) throws Exception
{
model.withModel().factory(myStateMachineModelFactory());
}
#Bean
public StateMachineModelFactory<String, String> myStateMachineModelFactory()
{
return new UmlStateMachineModelFactory("classpath:my.uml");
}
....
I need to persist the state machine context in a database using JPA. In order to do this, I need to use StateMachinePersister.persist(). This method uses as its first input parameter a StateMachine instance. However, I'm not able to get the StateMachine instance from my StateMachineModelFactory. The class StateMachineFactory has a method named getStateMachine() while the class StateMachineModelFactory doesn't.
I didn't find neither a way to get a StateMachineFactory instance from a StateMachineModelFactory instance. Could anyone please help with some suggestions, ideally examples ? The documentation has different examples of how to do it but none for the case when the state machine is loaded from an UML file.
Kind regards,
Nicolas DUMINIL

Your configuration for StateMachineFactory should look like below
#Configuration
#EnableStateMachineFactory
public static class SsmConfig
extends EnumStateMachineConfigurerAdapter<States, Events> {
#Override
public void configure(StateMachineStateConfigurer<States, Events> states)
throws Exception {
states
.withStates()
.initial(States.S1)
.end(States.SF)
.states(EnumSet.allOf(States.class));
}
}
And then you can simply inject StateMachineFactory everywhere and just get state machine.
class SomeService {
#Autowired
private StateMachineFactory<States, Events> factory;
void method() {
StateMachine<States,Events> stateMachine = factory.getStateMachine();
stateMachine.start();
}
}
More information can be found here.

Your configuration, which extends StateMachineConfigurerAdapter is an "adaptation" or modification of the auto-configured Spring state machine.
In order for your configuration to be picked-up by spring you need to annotate it with #Configuration.
You also have to enable the State Machine auto-configuration, which happens with #EnableStateMachie annotation.
#Configuration
#EnableStateMachine
public class MyStateMachineConfig extends StateMachineConfigurerAdapter<String, String> {
You can refer to the official documentation for more details.
Once this is active, you can inject the StateMachine as a dependency.

Related

how to conditionally not create beans in spring boot?

In my application, I have a component that reads data from other system when the application is started.
However, during testing, I don't want this component to be created
#Component
#Slf4j
public class DeviceStatisticsSyncHandler {
#EventListener
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
#Value("${test.mode:false}")
public boolean serviceEnabled;
}
I can use condition to solve this, but other code readers need to understand, so I don't think this is a very good method:
#EventListener(condition = "#deviceStatisticsSyncHandler .isServiceEnabled()")
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
public boolean isServiceEnabled() {
return !serviceEnabled;
}
#Value("${test.mode:false}")
public boolean serviceEnabled;
My application doesn't use Profiles, is there any other method to solve this problem.
Spring Boot version:2.1.3
One possible option is not to load the DeviceStaticsticsSyncHandler at all if you're in a test mode.
The "test.mode" is not a good name here, because the production code contains something tightly bound to the tests.
How about the following approach:
#Component
#ConditionalOnProperty(name ="device.stats.handler.enabled", havingValue = "true", matchIfMissing=true)
public class DeviceStatisticsSyncHandler {
// do whatever you need here, but there is no need for "test.mode" enabled related code here
}
Now in Tests you can define a test property "device.stats.handler.enabled=false" on the test itself or even place that definition in src/test/reources/application.properties so it will be false for all tests in the module.
An obvious advantage is that this definition is pretty much self explanatory and can be easy understood by other project maintainers.
for me, it's not the case of the condition rather environment-related. I will solve this problem using spring profile.
Step 1: Create an Interface first
public interface DeviceStatisticsSyncHandler {
public void handle(ApplicationReadyEvent event);
}
Step 2: Create an Implementation for production
#Component
#Profile("!test")
public class DeviceStatisticsSyncHandlerImpl implements DeviceStatisticsSyncHandler {
#EventListener
#Override
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
}
step 3: create an implementation of test
#Component
#Profile("test")
public class DeviceStatisticsSyncHandlerTestImpl implements DeviceStatisticsSyncHandler {
#EventListener
#Override
public void handle(ApplicationReadyEvent event) {
//do Nothing
}
}
final step
All you need to do is set/toggle the property
-Dspring.profiles.active=test
or
-Dspring.profiles.active=prod
I found a way to achieve this without any further external configuration required.
The idea is to create a general configuration that applies to all integration tests and use #MockBean there to replace the real bean. So one should create a class like this under the test classpath (i.e. that is not scanned during normal application launch):
#Configuration
public class IntegrationTestConfiguration
{
#MockBean
public DeviceStatisticsSyncHandler deviceStatisticsSyncHandler;
}
I was actually surprised that #MockBean can be used here, but the Javadoc explicitly points that out: Can be used as a class level annotation or on fields in either #Configuration classes, or test classes that are #RunWith the SpringRunner..

Spring Boot - the best way to read data from database on startup

I would like to read data to List or Map from database on startup.
Which is the best way to do it? The Spring Boot version is 5.
Is the below solution is good?
#Component
public class ApplicationStartup
implements ApplicationListener<ApplicationReadyEvent> {
/**
* This event is executed as late as conceivably possible to indicate that
* the application is ready to service requests.
*/
#Override
public void onApplicationEvent(final ApplicationReadyEvent event) {
// here your code ...
return;
}
}
I'd like to storage data on static class, but I have doubt that is the best solution.
I don't quite understand what is your motive for doing so but for doing so you can create a bean using #Component and in that bean create a method with annotation #PostConstruct. you can do whatever you want in this method.
Using the ApplicationRunner interface is the best way to run code once the Spring boot context has loaded.
#Component
public class ApplicationStartup implements ApplicationRunner {
#Override
public void run(ApplicationArguments args) throws Exception {
}
}
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html#boot-features-command-line-runner

Using context beans within a Condition or a ConfigurationCondition

I wish to use a org.springframework.context.annotation.Condition interface to disable/enable at startup some of my #Configuration classes. Example below:
public class Condition implements ConfigurationCondition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//Gets the configuration manager from the context
RapidConfDocConfiguration.DocConf docConf = context.getBeanFactory().getBean(DocConf.class);
return docConf.isEnabled();
}
}
#Configuration
public class ConfDocConfiguration {
#Bean
public DocConf docConf() {
return new DocConf(true);
}
}
#Configuration
#Import(ConfDocConfiguration.class)
#Conditional({Condition.class})
public class RapidSwaggerConfiguration {
//Some configurations
}
The problem i have here is that the Condition is executed before that any of the context is instantiated and then the 'DocConf' is not yet present. The Condition works well if i read an environment variable or something like that.
So, is it possible to use this kind of condition based on a spring bean of the context ?
This is not possible because your DocConf class is not loaded as a Configuration, #Bean annotated beans are created after all configuration is done.
For this to work, your DocConf class would have to be added to the context as a Spring configuration, so including it via #Bean from a different configuration cannot work, because non-configuration Beans are only created after the configuration phase.
Consider this article: http://www.javarticles.com/2016/01/spring-configuration-condition-example.html
Another example at http://javapapers.com/spring/spring-conditional-annotation/

Spring Autowiring not working for Abstract classes

I have a project where I have an interface, an Abstract class implementing the same interface and then a set of concrete classes which implement this interface and extend the Abstract Class.
public interface Invoice
{
void process();
}
#component
public abstract class AbstractInvoice(){
#Resource
protected Writer writer;
protected validateInvoice(){
//some implementation
}
}
#Component
public Class TypeAInvoice() extends AbstractInvoice implements Invoice{
#Override
public void process(){
//... some code
writer.write();
}
}
public Interface Writer(){
public void write();
}
#Component
public class CDWriter implements Writer{
#Override
public void write() { /* implementation.....*/}
}
Spring file has a component scan for the package.
<context:annotation-config>
<context:component-scan base-package="com.xyz" />
I am using a factory to get an instance of TypeAInvoice invoice
Now calling invoice.process() gets a NPE when getting to write.write()
I am not sure what am I missing here. I tried to see the component scan and scope and could not find anything conceptually wrong.
I am using a factory to get an instance of TypeAInvoice invoice
Depending on what your Factory does, this may be the problem. If the Factory creates a new TypeAInvoice, Spring wiring doesn't apply. You have to query the Spring context for the Bean. One way (though not very pretty) is to use ContextLoader:
return ContextLoader.getCurrentWebApplicationContext().getBean(TypeAInvoice.class)
I'd say static Factories and Spring don't go to well together. Spring stands for the Inversion of Control pattern, while Factories stand for the Service Locator pattern. I'd suggest that you get rid of your factories and autowire your Spring Beans.
Everything is good, except for the fact you use a factory to get the TypeAInvoice. If you create it like TypeAInvoice typer = new TypeAInvoice() then spring knows nothing of it, the Writer is not autowired, there for you get the NullPointerException. You should get the bean from the spring application context.
In my case, inside a Spring4 Application, i had to use a classic Abstract Factory Pattern(for which i took the idea from - http://java-design-patterns.com/patterns/abstract-factory/) to create instances each and every time there was a operation to be done.So my code was to be designed like:
public abstract class EO {
#Autowired
protected SmsNotificationService smsNotificationService;
#Autowired
protected SendEmailService sendEmailService;
...
protected abstract void executeOperation(GenericMessage gMessage);
}
public final class OperationsExecutor {
public enum OperationsType {
ENROLL, CAMPAIGN
}
private OperationsExecutor() {
}
public static Object delegateOperation(OperationsType type, Object obj)
{
switch(type) {
case ENROLL:
if (obj == null) {
return new EnrollOperation();
}
return EnrollOperation.validateRequestParams(obj);
case CAMPAIGN:
if (obj == null) {
return new CampaignOperation();
}
return CampaignOperation.validateRequestParams(obj);
default:
throw new IllegalArgumentException("OperationsType not supported.");
}
}
}
#Configurable(dependencyCheck = true)
public class CampaignOperation extends EO {
#Override
public void executeOperation(GenericMessage genericMessage) {
LOGGER.info("This is CAMPAIGN Operation: " + genericMessage);
}
}
Initially to inject the dependencies in the abstract class I tried all stereotype annotations like #Component, #Service etc but even though Spring context file had ComponentScanning for the entire package, but somehow while creating instances of Subclasses like CampaignOperation, the Super Abstract class EO was having null for its properties as spring was unable to recognize and inject its dependencies.After much trial and error I used this **#Configurable(dependencyCheck = true)** annotation and finally Spring was able to inject the dependencies and I was able to use the properties in the subclass without cluttering them with too many properties.
<context:annotation-config />
<context:component-scan base-package="com.xyz" />
I also tried these other references to find a solution:
http://www.captaindebug.com/2011/06/implementing-springs-factorybean.html#.WqF5pJPwaAN
http://forum.spring.io/forum/spring-projects/container/46815-problem-with-autowired-in-abstract-class
https://github.com/cavallefano/Abstract-Factory-Pattern-Spring-Annotation
http://www.jcombat.com/spring/factory-implementation-using-servicelocatorfactorybean-in-spring
https://www.madbit.org/blog/programming/1074/1074/#sthash.XEJXdIR5.dpbs
Using abstract factory with Spring framework
Spring and Abstract class - injecting properties in abstract classes
Inject spring dependency in abstract super class
Spring autowire dependency defined in an abstract class
Spring can you autowire inside an abstract class?
Please try using **#Configurable(dependencyCheck = true)** and update this post, I might try helping you if you face any problems.
So precisely my point here is you don't need to get a bean from spring context all the time.

How to use #Autowired to dynamically inject implementation like a factory pattern

I am fairly new to Sprint and am using Spring 3.x and roo1.1.1 for my application.
I have multiple implementation of an interface which would be #Autowired into other different classes. I would only be able to decide which implementation to go with at the runtime. This should be achieved with like a factory pattern.
public interface SomeInterface {
public void doSomething();
}
Implementation 1.
public class SomeOb implements SomeInterface {
public void doSomething() {
//Do something for first implementation here
}
}
Implementation 2.
public class SomeOtherOb implements SomeInterface {
public void doSomething() {
//Do something for first implementation here
}
}
Now in my service i needed this Autowired like
#Service
public class MyService {
#Autowired
SomeInterface ob;
//Rest of the code here
}
1) The logic to choose which implementation to be Autowired is only know runtime, so i cannot use the #Qualifier annotation to qualify this.
2) I tried to create a FactoryBean like
public class SomeFactoryBean implements FactoryBean<SomeInterface> {
#Override
public SomeInterface getObject() throws Exception {
if(/*Somecondition*/) {
return new SomeOb();
} else
return new SomeOtherOb();
}
#Override
public Class<? extends SomeInterface> getObjectType() {
if(/*Somecondition*/) {
return SomeOb.class;
} else
return SomeOtherOb.class;
}
#Override
public boolean isSingleton() {
return false;
}
}
In the applicationContext.xml i have the tag mentioned.
When i run the webserver i run into an error like
No unique bean of type [com.xxxx.xxxx.SomeInterface] is defined: expected single matching bean but found 3: [xxxx, xxxxxxx, xxxxFactory]
Can anyone please help me to resolve this issue. If i am not doing this right please direct me to do this the right way.
Thanks and appreciate any help,
jjk
Thanks for the suggestion. I was able to solve the problem with help from a colleague. What i was doing wrong
I had the implementation of the SomeInterface with #Service. So this was picked up by the spring scanner and added to the bean.
During trial and error i removed the #Component annotation from by FactoryBean implementation.
After making these changes it worked like a charm.
return true from isSingleton() if you only need one implementation of the bean for a given instance of your application
But I question your design.
I would always use properties files to switch out implementations like this. I once had to implement CAPTCHA integration for a site. We were prototyping with the JCaptcah and ReCAPTCHA APIs. I created a new interface that contained just the functionality we needed and then created implementations for both APIs. Using a placeholders in the Spring configuration file and Maven profiles, we could switch out the implementation class at compile time or deployment time, for example, mvn jetty:run -DcaptchaImpl=recaptcha or -DcaptchaImpl=jcaptcha.
Without knowing the task that you want to accomplish, it's hard to provide more advice.

Resources