I have a scheduled job running in liferay 6.1.2 which requires spring injection.
Sample code -
import com.liferay.portal.kernel.messaging.Message;
import com.liferay.portal.kernel.messaging.MessageListener;
import com.liferay.portal.kernel.messaging.MessageListenerException;
import java.lang.reflect.InvocationTargetException;
public class ScheduledJob implements MessageListener {
#Autowired
private SomeService service;
#Override
public void receive(final Message msg) throws MessageListenerException {
try {
service.someMethod();
} catch (final IllegalAccessException e) {
} catch (final InvocationTargetException e) {
}
}
}
The injected service is not initialized through application context and is always null.
Auowiring works perfectly fine for other classes. Only fails for ScheduledJob.
Anyone knows the solution?
Thanks
I haven't try it my self but you can try to implement ApplicationContextAware interface by your ScheduledJob class and get injected beans as described here.
Create one class in another package and in that class autowire your service with the help of the constructor by putting #component annotation on class and #Autowire annotation on parameterized constructor where you will pass serviceobject in paramater.
In this class write one static method which will use your service.
Put this package name in componentscan tag in spring xml file.
Now in receive method you have to call that method with class name as method is static.
Related
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);
}
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);
}
}
I'm trying to print a message after the application startup with #PostConstruct, but nothing is printed.
package dev.renansouza.server;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
#Singleton
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
I have read that #PostConstruct is Lazy. Does this mean that I need to do
something else for this to work?
You can also use #EventListener annotation to acheive what you what, if using #PostConstruct is not that important to you.
For example in your case, you can add following code in any class to listen for application startup event.
#EventListener
void onStartup(ServerStartupEvent event) {
println("Hey, I work from anywhere in project..")
}
Code shared above is in Groovy
Keep in mind, the event listener added in main application class is usually called first from what I have observed.
The problem (aka feature) is, as you already mentioned, the lazy loading.
I see two solutions:
You have to do something to cause that bean to be initialized.
Change the scope of the bean from #Singleton to #Context
Micronaut has a few built-in scopes (see https://docs.micronaut.io/latest/guide/index.html#scopes) and the documentation of #Context states (see https://docs.micronaut.io/latest/api/io/micronaut/context/annotation/Context.html)
Context scope indicates that the classes life cycle is bound to that of the BeanContext and it should be initialized and shutdown during startup and shutdown of the underlying BeanContext.
Micronaut by default treats all Singleton bean definitions as lazy and will only load them on demand. By annotating a bean with #Context you can ensure that the bean is loaded at the same time as the context.
package dev.renansouza.server;
import javax.annotation.PostConstruct;
import io.micronaut.context.annotation.Context;
#Context
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
See the project at https://github.com/jeffbrown/renansouzapostconstruct.
https://github.com/jeffbrown/renansouzapostconstruct/blob/master/src/main/java/renansouzapostconstruct/ServerService.java
package renansouzapostconstruct;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
#Singleton
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
https://github.com/jeffbrown/renansouzapostconstruct/blob/master/src/main/java/renansouzapostconstruct/DemoController.java
package renansouzapostconstruct;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.HttpStatus;
#Controller("/demo")
public class DemoController {
private ServerService serverService;
public DemoController(ServerService serverService) {
this.serverService = serverService;
}
#Get("/")
public HttpStatus index() {
return HttpStatus.OK;
}
}
When you start the app you won't see the message printed to standard out because the service bean won't have been initialized. Send a request to http://localhost:8080/demo/ and then you will see the message printed to stdout.
I hope that helps.
I am trying to use guice and I want to create a Singleton of random webservice client in Java Play 2.6
For now I have my WS client and it loads as a java play module. When I run the application, no problem, my client is able to use the java play Configuration (com.typesafe.config.Config), which is injected. But if I try to use my client anywhere else, I get an error saying No implementation for com.typesafe.config.Config was bound .
Here is my (very simple) client :
import play.Logger;
import com.typesafe.config.Config;
#Singleton
public class MyClient {
final Config config;
#Inject
public MyClient(Config config) {
this.config = config;
Logger.warn("constructor called")
Logger.warn("Some config param:"+config.getString("some_param"))
}
public void doSomething() {
Logger.warn("doSomething() called")
}
}
My Module implementing Guice's AbstractModule :
import com.google.inject.AbstractModule;
public class MyClientModule extends AbstractModule {
#Override
protected void configure() {
bind(MyClient.class).asEagerSingleton();
}
}
When I tell Play to use it as a module in applicationf.conf, it works (i get the "Constructor called" and the "Some config param" warning logs in the console):
play {
modules {
enabled += external.MyClientModule
}
}
But If I try to call it from my HomeController :
public class HomeController extends Controller {
public Result index() {
Injector myClientInjector = Guice.createInjector(new MyClientModule());
MyClient myClient = myClientInjector.getInstance(MyClient.class);
return ok(views.html.index.render());
}
}
then I get the following error :
[CreationException: Unable to create injector, see the following errors:
1) No implementation for com.typesafe.config.Config was bound.
while locating com.typesafe.config.Config
for the 1st parameter of external.MyClient.<init>(MyClient.java:121)
at external.MyClientModule.configure(MyClientModule.java:8)
1 error]
I'm pretty sure there are a few things wrong here, so what would be the correct way to bind it and then use it ?
In the HomeController, use constructor injection :
#Inject
public HomeController (final MyClient myclient) {
this.myclient = myclient;
}
You can annotate 1 constructor like this, so it needs to contain all the classes you want to inject. You can combine constructor and field injection, but that is not recommended.
The asEagerSingleton() bind means that it's bound as fast as possible. In this case, Config is not bound yet, so it fails.
Use
bind(MyClient.class).in(Singleton.class)
which binds it as a singleton, when it's needed.
In the HomeController, use constructor injection :
#Inject
public HomeController (final MyClient myclient) {
this.myclient = myclient;
}
You can annotate 1 constructor like this, so it needs to contain all the classes you want to inject. You can combine constructor and field injection, but that is not recommended.
A little bit new with spring. When I instantiate a bean via interface, it doesn't seem to get events, if however, I use actual class implementing the interface, then the event is received. Why is this? Code below.
package javabeans.di;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStartedEvent;
public class HelloWorldImpl implements HelloWorld, ApplicationListener<ContextStartedEvent> {
private String msg;
public HelloWorldImpl(String s){
msg = s;
}
#Override
public void printHelloWorld() {
System.out.println("Hello : " + msg);
}
public void onApplicationEvent(ContextStartedEvent event) {
System.out.println("ContextStartedEvent Received");
}
}
Here is the calling code:
public static void main(String[] args) {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(HelloWorldConfig.class);
// Let us raise a start event.
ctx.start();
HelloWorld obj = (HelloWorld) ctx.getBean("helloWorld");
obj.printHelloWorld();
ctx.stop();
}
Config class:
#Configuration
public class HelloWorldConfig {
#Bean
#Scope("prototype")
public HelloWorld helloWorld(){
return new HelloWorldImpl("Hello java beans");
}
}
The interface:
package javabeans.di;
public interface HelloWorld {
void printHelloWorld();
}
"ContextStartedEvent Received" never gets shown if the bean has a prototype scope.
NOTE: If I change return type of bean method to HelloWorldImpl in the config class, and also change HelloWorld to HelloWorldImpl inside main (two occurrences - basically on the line where I call getBean), then this works also with prototype beans.
Why would that be? Additionally if I create two instances of HelloWorldImpl in main, in a manner described in this paragraph, still the event is received only once (but that might be separate issue).
When using java based configuration what happens is very early in the process the #Configuration classes are read with ASM (they aren't loaded through a class loader yet). Based on that read bytecode Spring creates the bean definitions and proxy based classes.
A #Bean method (regardless where it is) is basically the same as a FactoryBean. It acts more or less in the same way. When the meta data is created it does so by inspecting the method signature and using the return type to create a factory. This return type is basically used for the getObjectType method of a FactoryBean. And this the result of that method is used to determine what the bean supports.
Now when return HelloWorld as a type you get a factory creating beans of that type. When using HelloWorldImpl you will get a factory creating beans of that type. The first doesn't contain the ApplicationListener interface and as such is ignored by spring, the second however does (it is detected at that point of generating the (auto) configuration meta data).
So when using #Configuration with #Bean it is important to be as specific as possible about the return type.
Isn't this because the interface itself doesn't have the listening method?
Shouldn't you
package javabeans.di;
public interface HelloWorld extends ApplicationListener<ContextStartedEvent>{
void printHelloWorld();
public void onApplicationEvent(ContextStartedEvent event);
}
And then #Override in the implementing class?
This is just a guess, but the ApplicationListener interface is only available for the concrete implementation class HelloWorldImpl. Thus when spring creates the helloWorld bean it creates a type HelloWorld which does not have the ApplicationListener interface and thus the event will not be propagated to this bean.
Let the HelloWorld interface extend ApplicationListener and you should receive an event.