How to update row value using JPA repository outside the controller? - java

I was trying to update the table row data from outside the controller (Inside some threads) and getting 'NullPointerException' always.
Thread code:
public class S3Thread implements Runnable {
#Autowired
private IAutomationService automationService;
#Override
public void run() {
Automation config = new Automation("user1","success");
automationService.updateAutomation(config);
}
}
NullPointer exception thrown on below line:
automationService.updateAutomation(config);
Note: I was able to create/update from the controller class.Only in Thread.

Well, this is the classical Why is my Spring #Autowired field null case. You create the S3Thread instance by yourself, and thus, no beans are injected into it.
Considering you're trying to just do something in a separate thread, you can consider using #Async:
#Async
public void updateAutomationConfiguration() {
Automation config = new Automation("user1", "success");
automationService.updateAutomation(config);
}
Notes:
You have to add the #EnableAsync annotation to any configuration class (eg. your main class) to make this work.
Spring uses proxying by default, which means that you can't add this updateAutomationConfiguration() class to your controller itself. Direct calls to methods within the same bean bypass the proxied logic. The solution is to put this method in a separate bean which can be autowired and invoked from within the controller. I've provided more detailed answers about alternative solutions in this answer.
Spring also has a getting started guide for creating asynchronous methods.
Alternatively, there are also some ways to execute asynchronous calls within controllers, for example by using CompletableFuture within a controller:
#PutMapping("/automation/configuration")
public CompletableFuture<String> updateAutomationConfiguration() {
return CompletableFuture.supplyAsync(() -> {
Automation config = new Automation("user1", "success");
return automationService.updateAutomation(config);
});
}
Related: How to create a non-blocking #RestController webservice in Spring?

Spring does not scan your runnable as it is not annotated with #Component.Try annotating it with #Component/#Service.
Don't forget to set scope required scope though!

There are 2 potential solutions to your problem:
Either you need to make S3Thread class a service by annotating it with #Service or #Component and autowiring it on the calling class, or you can alternatively use the constructor for initializing your automationService, e.g. private IAutomationService automationService = new AutomationService();

Since your thread class is not managed by spring you will not be able to inject the spring managed beans in the S3Thread class.
In order to do that you need to create a class or factory which should be hooked into the spring life cycle.
Once you have the hold of that class you can get the appropriate bean and pass the reference onto/or used in the S3Thread class directly. Something like this
#Component
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext appContext)
{
ctx = appContext;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
}
public class S3Thread implements Runnable {
#Override
public void run() {
Automation config = new Automation("user1","success");
IAutomationService automationService=
ApplicationContextUtils.getApplicationContext().getBean(IAutomationService .class);
automationService.updateAutomation(config);
}
}

Related

How to test #PreDestroy and #Bean methods inside a class

I have the following methods inside a class
#Bean(name = "boggle")
public BoggleImpl createBoggleClient() {
BoggleBuilder builder = new BoggleBuilder()
.setRegistryId(getRegistryId())
.setRegistryPassword(getPassword())
return new BoggleFeatureImpl(builder.build());
}
and am using the bean inside a class such as
class A {
private final Boggle boggle;
#PreDestroy
public void destroy() {
if (boggle != null) {
boggle.closeConnection();
}
}
}
Now my code coverage in unit tests show these methods as not covered. Not sure what can i do to cover these methods. Any pointers on the same.
Using SpringJUnit4ClassRunner the ApplicationContext is shared between all the running test cases and the #PreDestroy is called only when the ApplicationContext is closed.
Spring has this behavior because you can be working in a big application which has a slow startup and can be costly to create a new context.
You could annotate your test method with the #DirtiesContext annotation.
From it's javadoc:
Test annotation which indicates that the ApplicationContext associated
with a test is dirty and should therefore be closed and removed from
the context cache.
Use this annotation if a test has modified the
context — for example, by modifying the state of a singleton bean,
modifying the state of an embedded database, etc. Subsequent tests
that request the same context will be supplied a new context.
In this example, class A must be a spring bean. Methods annotated with #PreDestroy are called by the application context when it gets closed. Usually, this happens when the application gets shut down gracefully.
So basically you can "simulate" the situation of closing the application context even from a simple unit test, you don't have to start spring in the test for this.
Treat this method as a regular method with some code regardless of the fact that its called by spring:
class A {
private final Boggle boggle;
public class A(Boggle boggle)
{this.boggle = boggle;}
#PreDestroy
public void destroy() {
if (boggle != null) {
boggle.closeConnection();
}
}
}
Then a test can look like this:
class ATest {
#Test
public void test_boggle_closes_connection_when_the_bean_gets_destroyed() {
// given:
Boggle boggle = Mockito.mock(Boggle.class);
A underTest = new A(boggle);
// when:
underTest.destroy();
// then: verify that boggle closes connection
Mockito.verify(boggle, times(1)).closeConnection();
}
}

Spring inject Repository mixed with normal property in constructor

I have Service class and Repository interface (Spring Data). I have also one abstract class:
public abstract class TestingMethod {
public TestingMethod() {
timeSum = 0;
}
protected long timeSum;
}
And class that extends it:
#Component
public class LimitTestingMethod extends TestingMethod {
#Autowired
private GeoTestDataRepository geoTestDataRepository;
private final int limitSize;
public LimitTestingMethod(int limitSize) {
super();
this.limitSize = limitSize;
}
}
In my Service I want to create instance of LimitTestingMethod and set its argument limitSize.
Something like:
LimitTestingMethod ltm3 = new LimitTestingMethod(3);
LimitTestingMethod ltm10 = new LimitTestingMethod(10);
But I got error:
Description: Parameter 0 of constructor in
com.exence.postgiscma.testingMethod.LimitTestingMethod required a bean
of type 'int' that could not be found. Action: Consider defining a
bean of type 'int' in your configuration.
Is that possible to achieve something like I want?
All best!
//EDIT
As I can see in comments it's bad approach. So maybe someone will give me advise how to project this better?
Is this good solution to pass repo as argument in constructor (I guess that not, but I can't get the idea how to do this better)?
LimitTestingMethod ltm3 = new LimitTestingMethod(3, geoTestDataRepository);
Is there a good and elegant solution?
As you are creating instances outside the scope of Spring your current solution won't work. The error comes from the fact that you have annotated it with #Component, it will detect it at startup and tries to create a bean, and fails.
To solve this you can do 1 of 2 things.
Let Spring handle the creation of the beans by using the ApplicationContext as a factory, providing additional arguments and make the bean prototype scoped.
Let Spring handle the injection after you manually created the instance using the ApplicationContext.
Use ApplicationContext as a factory
First make your bean a prototype so that it will be constructed when needed.
#Component
#Scope(
ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class LimitTestingMethod extends TestingMethod { ... }
Now an instance won't be created during startup. In your service inject the ApplicationContext and use the getBean method to get your desired instance.
public class Service {
#Autowired
private ApplicationContext ctx;
public void yourMethod() {
LimitTestingMethod ltm3 = ctx.getBean(LimitTestingMethod.class, 3);
LimitTestingMethod ltm10 = ctx.getBean(LimitTestingMethod.class, 10);
}
}
This will let Spring create the instance using the value passed in for the constructor and do the autowiring.
Injection after creation
Another solution is to manually create the instances and after that let Spring handle the auto wiring. You will lose the AOP abilities with this and will get only auto wiring.
First remove the #Component annotation from your LimitTestingMethod so it won't get detected during startup.
public class LimitTestingMethod extends TestingMethod { ... }
Now in your service autowire the ApplicationContext and after creating your bean use that to inject the dependencies.
public class Service {
#Autowired
private ApplicationContext ctx;
public void yourMethod() {
LimitTestingMethod ltm3 = new LimitTestingMethod(3);
LimitTestingMethod ltm10 = new LimitTestingMethod(10);
ctx.getAutowireCapableBeanFactory().autowireBean(lmt3);
ctx.getAutowireCapableBeanFactory().autowireBean(lmt10);
}
}
Both will achieve what you want, however, now your code directly depends on the Spring API. So instead of doing this, you are probably better of with another option and that is to inject everything for the LimitTestingMethod through the constructor and pass the repository yourself.
Use constructor to create an instance
public class LimitTestingMethod extends TestingMethod {
private final GeoTestDataRepository geoTestDataRepository;
private final int limitSize;
public LimitTestingMethod(int limitSize, GeoTestDataRepository geoTestDataRepository) {
this.limitSize=limitSize;
this.geoTestDataRepository=geoTestDataRepository;
}
}
Then you can simply autowire the repository in your service class and create the instances as needed (or create a factory which contains the complexity of creating this object).
public class Service {
#Autowired
private GeoTestDataRepository repo;
public void yourMethod() {
LimitTestingMethod ltm3 = new LimitTestingMethod(3, repo);
LimitTestingMethod ltm10 = new LimitTestingMethod(10, repo);
}
}

How could I take an instance of class in Spring Boot?

It is a class which instance is connected to the external service and it is listening constantly of it.
#Component
public class Service extends PollingBot {
#Value("${token}")
private String token;
#Override
public void onUpdateReceived(Update update) {
if (update.hasMessage()) {
}
}
public void sendMessageToUser(String message) {
try {
execute(sendMessage);
} catch (ApiException e) {
}
}
}
You could see that there is a method called sendMessageToUser which send message. It could not be static because execute method not allow static context. This method could not be separeted to other class. /
So, I have to call this method from other class. However I don't want to create additional instance of Service class otherwise I have two instances which are listen for updates, but I want it is sole class instance doing so.
I have tried to run a Application Context and run method from it, but it was not worked.
So, my question is very simple. How could I run this class non-static(!) method from other class?
By default all spring managed beans are singleton. You need to use #Autowired to inject the bean into other and then you can call the methods of that bean.
#Autowired
private Service service;
public void sendMessage(String message){
service.sendMessageToUser(message);
}
You can use #Autowired annotation to call a method of a bean class(component) in Spring. Also, as mentioned by default beans are singleton in spring so you don't need to worry about creating a single instance explicitly every time.
Try to use the below code in the calling class:
#Autowired
private Service service;
public void sendText() {
service.sendMessage(message);
}

Spring Boot: Initially data parsing

I am searching for a way to read and parse a lot of data when the spring boot app is starting and be able to use these data later in other classes.
I started with a class DataRepository.java and annotated it with #Service to be able to inject it later. I'm planning to read the data here and to inject it in any other class I need the data.
But how can I achieve to parse the data just once and at app startup? The spring boot app should only be reachable if the parsing is done.
Your approach with #Service is 100% appropriate.
By default all beans are singletons, so if you parse data on bean creation (in constructor) it will be parsed only once, and this info can be used in other beans by simple injection.
Please note that if during data parsing you have to use other beans, you should be confident that all beans are completely constructed. For that you should use approach proposed by #jreznot:
https://stackoverflow.com/a/51783858/5289288
You can use ContextStartedEvent and handle it:
#Component
public class ContextStartedListener implements ApplicationListener<ContextStartedEvent> {
#Override
public void onApplicationEvent(ContextStartedEvent cse) {
System.out.println("Handling context start event. ");
}
}
See also: https://www.baeldung.com/spring-events
By default all beans in spring context are singletons. Spring guarantees that it will creates a bean just ones during context loading.
For example if you will have few contexts in your application it creates one instance for every context.
If you have just one context you can use these approaches:
initialize data in constructor. Data will initialized and ready to
use just after bean's instance creation.
#Component
public class DataRepository {
public DataRepository() {
... init data
}
}
use #Bean annotation withinit method. Allows you don't stick to Spring in
your data repository and initialize data after all beans were created.
public class DataRepository {
public void init() {
... init data
}
}
#Configuration
public class DataRepositoryConfiguration {
#Bean(initMethod = "init")
public DataRepository dataRepository() {
return new DataRepository();
}
use #Bean annotation and invoke init method. Allows you don't stick to
Spring in your data repository, but #Autowired field will uninitialized.
public class DataRepository {
public void init() {
... init data
}
}
#Configuration
public class DataRepositoryConfiguration {
#Bean
public DataRepository dataRepository() {
DataRepository dr = new new DataRepository();
dr.init();
return dr;
}
}
use #PostConstruct annotation. Initialize data after all beans was
created.
public class DataRepository {
#PostConstruct
public void init() {
... init data
}
}
Exception thrown during initializing will stop Spring's context initializing
You can use PostConstruct on any bean. For example
#Component
class DataLoad {
......
......
#PostConstruct
public void parseData() {
...... do your stuff here.......
}
}
With this the code inside parseData will be called only once. This is a very common way to do things in scenarios like when you want to load some configuration data from database at the start of the application and do it only once. In these cases you can #Autowired the repository class to the same class and use that in your #PostConstruct method and get data

#Asyn annotation with Spring

I am trying to use #Async annotation provided by spring. Going through some of the blogs I found there are the following constraints for using it:
It must be applied to public methods only
Self-invocation – calling the async method from within the same class – won’t work
I have a method which is getting called from the same class which I want to mark #Async. Is there any way of achieving it from the same class?
In Spring v4.3+ you can use self injection, and call the method on the self injected reference.
So for example:
#Component
public class SomeClass {
#Autowired
private SomeClass selfInjected;
public void someMethod() {
selfInjected.someOtherMethod();
}
#Async
public void someOtherMethod(){
...;
}
}
Updated as OP is using version before 4.3:
This will work for you.
#Component
public class SomeClass {
#Autowired
private ApplicationContext applicationContext;
private SomeClass selfInjected;
#PostConstruct
private void init() {
selfInjected = applicationContext.getBean(SomeClass.class);
}
}
Or
The other option is to extract the method to separate class and autowire it. I would personally explore this option before doing the above method.

Categories