Hi I need to send an email when my application starts and and email when my application stops.
Using spring...
public class Main {
public static void main(String[] args) {
FileSystemXmlApplicationContext fac = new FileSystemXmlApplicationContext("config/applicationContext.xml");
}
}
The rest is wired up through the application context...
I suppose I can just inject a simple bean that implements smart life cycle and send an email from within the start() and stop() methods?
You can simply use a bean in default singleton scope, and declare its init and destroy method. The bean need no adherence to Spring and could be something like :
public class StartAndStop {
public void onStart() {
// send the mail signaling start of application
...
}
public void onStop() {
// send the mail signaling stop of application
...
}
}
In xml config :
<bean class="org.example.StartAndStop" init-method="onStart" destroy-method="onStop"/>
And with Java configuration
#Configuration
public class Configurer {
#Bean(initMethod="onStart", destroyMethod="onStop")
StartAndStop startAndStop() {
return new StartAndStop();
}
... other beans configuration ...
}
Of course, you can also use spring to set properties on the bean ...
Spring automatically raises events in these sitatuations.
You can listen for events by creating an ApplicationListener bean:
#Component
public class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(final ContextRefreshedEvent event) {
// is called whenever the application context is initialized or refreshed
}
}
Spring raises the following application context events:
ContextClosedEvent (ApplicationContext gets closed)
ContextRefreshedEvent (ApplicationContext gets initialized or refreshed)
ContextStartedEvent (ApplicationContext gets stopped)
ContextStoppedEvent (ApplicationContext gets stopped)
See Standard and Custom Events documentation
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 have a particular class used to interface with a service that requires initialization. In the application lifecycle, the only place this makes sense is in the start of the application because the rest of the spring application cannot run without it. I had the idea to do this:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
try {
MyRequiredService mrs = new MyRequiredService();
mrs.connect(); // This will throw if it fails
run(MyApplication.class, args);
} catch(MyException e) {
System.out.println("Failed to connect to MyRequiredService!");
}
}
}
This will launch the service and attempt to connect but I have one big problem. How do I pass this class around the application? I need it's functions in the service endpoints I am writing.
I didn't see anything obvious and searching "passing class instance in spring boot application" turns up a bunch of unrelated topics.
Is there a smart, clean way to do this in spring boot? I apologize for a contrived example. The names of the service are unique enough I didn't want to violate any agreements.
You can make Spring do this for you. First, you need to annotate your class with #Service, so Spring will pick it up when scanning for classes.
Then, define an init() method and annotate it with #PostConstruct. Spring will instantiate your MyRequiredService class and call init()
#Service
public class MyRequiredService {
#PostConstruct
public void init() {
connect();
}
public void connect() {
// ...
}
}
You could call connect() from the constructor, but I don't like to define objects that may throw exceptions out of the constructor.
And then, you can use MyRequiredService in some other class by injecting it via the #Autowired annotation:
#Component
public class MyOtherClass {
private final MyRequiredService service;
public MyOtherClass(final MyRequiredService service) {
this.service = service;
}
// Other methods here.
}
This has the same overall effect as what you're trying to do above. If MyRequiredService fails, the application will not start up.
Make it a bean. Then it will be in the ApplicationContext which then you can pass to your desired other classes through the constructor
#Configuration
public class ApplicationConfiguration
{
#Bean
public MyRequiredService myRequiredService()
{
MyRequiredService mrs = new MyRequiredService();
try {
mrs.connect(); // This will throw if it fails
return mrs;
} catch(MyException e) {
log.error("Failed to connect to MyRequiredService!");
throw new IllegalStateException("MyRequiredService failed connection. Stopping startup");
}
}
#Bean
public SomeOtherService someOtherService(MyRequiredService mrs) {
return new SomeOtherService(mrs);
}
}
IMHO Instead of catching the error and logging it. I would throw it and stop the application from starting, but to keep with your example I added the throw IllegalStateException after the log.
Doing it this way Spring will create your MyRequiredService bean in the ApplicationContext then you can see I added as a parameter needed by the bean below that. Spring will grab that bean out of the ApplicationContext and supply it to the bean. If Spring doesn't find the bean in the ApplicationContext it will throw an error and stop the application from startup.
a class implements BeanFactoryPostProcessor which is init before normal bean
#Configuration
public class MyRequiredService implements BeanFactoryPostProcessor,
PriorityOrdered, InitializingBean {
#Override
public int getOrder() {
return Integer.MIN_VALUE;
}
public void connect() {
// ...
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
#Override
public void afterPropertiesSet() throws Exception {
connect();
}
}
I need to initialize beans in the Spring context after my application has started; currently, I initialize beans in a class with annotation #Configuration like this:
#Configuration
public class AppConfig {
#Inject
#Bean
public BeanA init(param1, param2, etc...) {
--- Code to construct bean A ---
}
#Inject
#Bean
public BeanB init(param1, param2, etc...) {
--- Code to construct bean B ---
}
}
But some beans I need to initialize after application startup so my approach is create a class listen to ApplicationReadyEvent event in Spring and put the code to initialize beans in that class.
#Configuration
class ApplicationStartingListener implements ApplicationListener<ApplicationReadyEvent>{
---- Code to init bean here ----
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
--- If I put init bean code in here, is it correct? ----
}
}
Is this the best way? Or there are some other better solutions?
I will enumerate other approaches in order to init beans, I grouped the approach in Standard Approach and Spring Boot Approach.
Standard Approach
#PostConstruct: it is just an annotation that triggers a method after bean is create, it doesn't allow input parameters.
#Bean(init-method="somInitMehotd"): this approach is totally related to Spring bean lifecycle and it is called after bean creation, if you are using another method with #PostConstruct annotation, then the #PostConstruct will be called first. This approach doesn't allow input parameters.
ApplicationListener: this interface allows to listen the standard events related to the Context Lifecycle, also it can listen customized events. For example: create a class MyAppListener and implements ApplicationListener<ContextRefreshedEvent> in this case the MyAppListener will implement an onApplicationEvent method that receives a ContextRefreshedEvent
Spring Boot Approach
The runners: There are two very useful interfaces CommandLineRunner and ApplicationRunner both of them will run after ApplicationContext is created both of them allows to inject beans as input parameters.
Spring boot listeners: Spring Application gives some additional events than the standards events that comes from the Application Context. One of the event is ApplicationReadyEvent and it is fire when the application is ready to receive request. In order to listen this events just implements the ApplicationListener using ApplicationReadyEvent as generic.
Here is the example:
MyBean class has different methods that will be called for each approach listed above, every method will call a print method and that method has a Thread.sleep in order to validate the order that every listener is called.
import javax.annotation.PostConstruct;
public class MyBean {
private String myVar="";
public MyBean(){
}
#PostConstruct
public void postConstructInit(){
this.myVar="Post init called";
print();
}
public void beanInit(){
this.myVar="Bean init called";
print();
}
public void contextInit(){
this.myVar="Context init called";
print();
}
public void runnerInit(){
this.myVar="Runner init called";
print();
}
public void bootListenerInit(){
this.myVar="Boot init called";
print();
}
public void print(){
System.out.println(this.myVar);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Here is the ContextRefreshListener class that will listen the ContextRefreshedEvent and handle it.
public class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
contextRefreshedEvent.getApplicationContext().getBean(MyBean.class).contextInit();
}
}
And it is the BootListener that will receive the ApplicationReadyEvent that comes from Spring Application.
public class MyBootListener implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
applicationReadyEvent.getApplicationContext().getBean(MyBean.class).bootListenerInit();
}
}
And finally the Spring Boot Application
#SpringBootApplication
public class StackoverflowBootApplication {
public static void main(String[] args) {
SpringApplication.run(StackoverflowBootApplication.class, args);
}
#Bean(name = "myBean", initMethod = "beanInit")
public MyBean getMyBean(){
return new MyBean();
}
#Bean
public ContextRefreshListener getContextRefreshedListener(){return new ContextRefreshListener();}
#Bean
public MyBootListener getBootListener(){return new MyBootListener();}
#Bean
public CommandLineRunner getRunner(ApplicationContext ctx){
return (args) -> {
ctx.getBean(MyBean.class).runnerInit();
};
}
}
The output is:
Post init called
Bean init called
Context init called
Runner init called
Boot init called
Post init called output comes from
#PostConstruct
public void init(){
this.initByPostconstruct="Post init called";
Bean init called comes from the initMethod value
#Bean(name = "myBean", initMethod = "beanInit")
public MyBean getMyBean(){
return new MyBean();
}
}
Context init called comes from ContextRefreshedEvent
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
contextRefreshedEvent.getApplicationContext().getBean(MyBean.class).contextInit();
}
Runner init called comes from CommandLineRunner
#Bean
public CommandLineRunner getRunner(ApplicationContext ctx){
return (args) -> {
ctx.getBean(MyBean.class).runnerInit();
};
}
Boot init called comes from ApplicationReadyEvent
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
applicationReadyEvent.getApplicationContext().getBean(MyBean.class).bootListenerInit();
}
All the listed scenarios were triggered by Spring, I didi'nt call any of the events directly, all of them were called by Spring Framework.
I have an on Object with an annotation
#ApplicationScoped
public class DbGraphConnectionLocator implements ServerStopTask {
...
}
This object create an embedded database.
I have tried to create an hook to shutdown database when i redeploy the application.
So i construct the class
#WebListener
public class UndeployHook implements ServletContextListener{
#Inject
DbGraphConnectionLocator dbGraphConnectionLocator;
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
}
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
try {
dbGraphConnectionLocator.executeStopTask();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Actually the method contextDestoyed is called but the object used in is not the original one DbGraphConnectionLocator created at start up, and this is causing me some trouble.
In my opinion with the annotation
#ApplicationScoped
public class DbGraphConnectionLocator implements ServerStopTask { ....}
The instance of DbGraphConnectionLocator have to be unique at application level so when i use it with a
#Inject
DbGraphConnectionLocator dbGraphConnectionLocator;
I have to found the same instance created on start up by my application, but this is not true another instance was created.
Anyway i resolved adding the
#PreDestroy
public void shutdownDB(){
graphDb.shutdown();
}
to DbGraphConnectionLocator.
For info i see that this method is called after the contextDestroyed method , and this means that when the contextDestroyed is called the original instance still exist, so i miss something....
any hint?
If the embedded db shall be created at application start and be shut down when the application is stopped, just do the following:
#ApplicationScoped
#Startup
public class AnyNameForYourApplicationClass {
#PostConstruct
public void connectDB() {
// creates the db connection at startup
}
#PreDestoy
public void disconnectDB() {
// disconnects from the db
}
}
This should do it.
Alternatively you could also use a CDI producer to create an application scoped db connection. Then you can disconnect from the db using a disposer method.
In your case the behavior is correct since when the web listener is called the application must still be alive. So the shutdownDB method with #PreDestroy is called afterwards when the application scoped bean is destroyed. And since you are referencing it in your listener, it must stay alive until the listener is destroyed.
I am having troubles invoking a method asynchronously in Spring, when the invoker is an embedded library receiving notifications from an external system. The code looks as below:
#Service
public class DefaultNotificationProcessor implements NotificationProcessor {
private NotificationClient client;
#Override
public void process(Notification notification) {
processAsync(notification);
}
#PostConstruct
public void startClient() {
client = new NotificationClient(this, clientPort);
client.start();
}
#PreDestroy
public void stopClient() {
client.stop();
}
#Async
private void processAsync(Notification notification) {
// Heavy processing
}
}
The NotificationClient internally has a thread in which it receives notifications from another system. It accepts a NotificationProcessor in its constructor which is basically the object that will do the actual processing of notifications.
In the above code, I have given the Spring bean as the processor and attempted to process the notification asynchronously by using #Async annotation. However, it appears the notification is processed in the same thread as the one used by NotificationClient. Effectively, #Async is ignored.
What am I missing here?
#Async (as well as #Transactional and other similar annotations) will not work when the method is invoked via this (on when #Async is used for private methods*), as long as you do not use real AspectJ compiletime or runtime weaving.
*the private method thing is: when the method is private, then it must been invoked via this - so this is more the consequence then the cause
So change your code:
#Service
public class DefaultNotificationProcessor implements NotificationProcessor {
#Resource
private DefaultNotificationProcessor selfReference;
#Override
public void process(Notification notification) {
selfReference.processAsync(notification);
}
//the method must not been private
//the method must been invoked via a bean reference
#Async
void processAsync(Notification notification) {
// Heavy processing
}
}
See also the answers for: Does Spring #Transactional attribute work on a private method? -- this is the same problem