Spring PostConstruct of a container - spring

How can I run some code inside a Spring Container after all beans has been loaded? I know I can use #PostConstruct for a single bean, but I would like to run that piece of code after all PostConstructs are called.
Is is possibile?
---UPDATE---
I tried to follow the ApplicationListener way, this is the implementation:
#Component
public class PostContructListener implements ApplicationListener<ContextRefreshedEvent> {
private static Logger log = LoggerFactory.getLogger(PostContructListener.class);
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
Collection<Initializable> inits= contextRefreshedEvent.getApplicationContext().getBeansOfType(Initializable.class).values();
for (Initializable initializable : inits) {
try{
log.debug("Initialization {} ",initializable.getClass().getSimpleName());
initializable.init();
}catch(Exception e){
log.error("Error initializing {} ",initializable.getClass().getSimpleName(),e);
}
}
}
}
Applying "Initializable" interface to all services I got what I needed, how every this way I broke all autowires, I cannot understand why but seems to be connected to the new "Initializable" interface:
java.lang.IllegalArgumentException: Can not set com.service.MyService field com.controller.RestMultiController.myService to com.sun.proxy.$Proxy41

I think you need this.
public class SpringListener implements ApplicationListener<ContextRefreshedEvent>{
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent ) {
// do things here
}
}
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-functionality-events

Related

Spring ExitCodeEvent using #EventListener not working

I am trying to catch the event code at the time my springboot app is destroyed. I have the following bean:
#Configuration
public class DestroyListenerConfig {
#Bean
DemoListener demoListenerBean() {
return new DemoListener();
}
private static class DemoListener {
#EventListener
public void exitEvent(ExitCodeEvent event) {
System.out.println("Exit code: " + event.getExitCode());
}
}
}
The bean is registering properly, but when I kill the application, the exitEvent() method is not invoked ( the system out never displays, or when run in debug mode from IDE, it never enters the method).
Am I leaving something out? My impression was this is all that is needed. Thanks.
ExitCodeEvent is published from org.springframework.boot.SpringApplication#exit. So, you need to manually call SpringApplication.exit as below.
#Autowired
ApplicationContext applicationContext;
#GetMapping("/shutdown")
void shutdown() {
SpringApplication.exit(applicationContext, () -> 100);
}
If you want to listen to a bean destroy event then you can use #PreDestroy as following:
Note that if you have multiple beans created for this class, you will get multiple triggers.
//Put this into a #Component(or inside RestController, Controller, Service etc) class
#javax.annotation.PreDestroy
public void destroy() {
System.out.println("Triggered - #PreDestroy.");
}
If your app is a webapp and you want to listen to shutdown event (ideally the contextDestroyed event) you can register a MyServletContextListener to ServletListenerRegistrationBean:
#Bean
ServletListenerRegistrationBean<ServletContextListener> servletListener() {
ServletListenerRegistrationBean<ServletContextListener> srb = new ServletListenerRegistrationBean<>();
srb.setListener(new MyServletContextListener());
return srb;
}
class MyServletContextListener implements ServletContextListener {
#Override
public void contextDestroyed(ServletContextEvent event) {
System.out.println("Callback triggered - ContextListener.");
}
}
Ref:
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java#L1332

Dependency injection in custom evaluator in logback in spring boot

I am writing a custom evaluator in which I want to autowire another bean. I am unable to do so as evaluator gets initialized by logger where as beans are initialized by spring context. Below is the sample of my code:
In logback-spring.xml:
<appender name="myAppender" class="ch.qos.logback.classic.net.SMTPAppender">
<evaluator class="com.package.CustomEvaluator">
<marker>FATAL</marker>
<interval>1000000</interval>
</evaluator>
</appender>
My custom evaluator:
#Slf4j
#Component
public class CustomEvaluator extends OnMarkerEvaluator {
#Autowired
private MyService myService;
#Override
public boolean evaluate(ILoggingEvent event) throws EvaluationException {
\\logic goes here
}
}
I am getting object of MyService always as null(which is expected). Is there any work around for this?
It don't think its possible because the Evaluator being an internal logback abstraction is not managed / initialized by spring, so obviously spring can't autowire anything into the evaluator.
In addition note, that logback gets initialized even before application context starts.
Of course you could provide some global holder class for the application context and set the context to it in the main method, and then get the reference to it in this evaluator, something like this:
public class ApplicationContextHolder {
private static ApplicationContext context;
// call it in main() method
public static void setContext(ApplicationContext ctx) {context = ctx;}
public static ApplicationContext getContext() {return context;}
}
class CustomEvaluator extends OnMarkerEvaluator {
public boolean evaluate(ILoggingEvent event) throws EvaluationException {
ApplicationContext ctx = ApplicationContextHolder.getContext();
if(ctx == null) {return false;} // not yet initialized / accessible
MyService myService = ctx.getBean(MyService.class);
}
}
But all-in-all I believe its a very ugly solution.
As a suggestion, I think you should consider refactoring of the logic so that the decision of whether to send an email based on logging event will be taken in the application (which is, I assume, spring boot driven so you have an access to the MyService)
Given the current implementation:
public foo() {
LOGGER.info("This should be sent by email");
}
I suggest a part of application:
#Component
public class MyLogic {
#Autowired MyService myService;
public void foo() {
if(myService.shouldSend()) {
LOGGER.info("This should be sent by email");
}
}
}

Wny am I getting "java.lang.IllegalStateException: No TransactionalEventListener annotation"?

I'm using Spring 4.3.8.RELEASE with Hibernate 5.1.5.Final. I want to have a method executed after another another transaction completes. That transaction is defined below
#Service("organizationService")
#Transactional
public class OrganizationServiceImpl implements OrganizationService, ApplicationEventPublisherAware
{
private ApplicationEventPublisher publisher;
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher)
{
this.publisher = publisher;
}
#Override
public void save(Organization organization)
{
...
// sync data with ThirdParty but only if something has definitelychanged on the SB
// side, in which case we want to send ThirdParty an update.
if (!hasSameAttributes)
{
publisher.publishEvent(new ThirdPartyOrganizationEvent(organization.getId()));
} // if
} // save
So here is the method that I want executed after the above transaction completes ...
#Service
public class ThirdPartyAPIServiceImpl implements ThirdPartyAPIService
{
#Override
#TransactionalEventListener
public boolean updateOrg(final ThirdPartyOrganizationEvent thirdPartyOrgEvent)
{
...
}
But when I load my application context I get this error
Caused by: java.lang.IllegalStateException: No TransactionalEventListener annotation found on method: public abstract boolean org.mainco.subco.myproject.service.ThirdPartyAPIService.updateOrg(org.mainco.subco.myproject.domain.ThirdPartyOrganizationEvent)
at org.springframework.transaction.event.ApplicationListenerMethodTransactionalAdapter.<init>(ApplicationListenerMethodTransactionalAdapter.java:55)
at org.springframework.transaction.event.TransactionalEventListenerFactory.createApplicationListener(TransactionalEventListenerFactory.java:55)
at org.springframework.context.event.EventListenerMethodProcessor.processBean(EventListenerMethodProcessor.java:159)
at org.springframework.context.event.EventListenerMethodProcessor.afterSingletonsInstantiated(EventListenerMethodProcessor.java:104)
... 34 more
Wbat do I need to do to get this configured properly?
Defining #TransactionalEventListener on interface method rather then on method implementing interface worked for me.

How to get a #Configuration object in ServletContextListener?

I am having here a ApplicationConfiguration class:
#Configuration
#PropertySource("classpath:application.properties")
public class ApplicationConfiguration implements DatabaseConfiguration {
#Value("${jdbc.driver}")
private String jdbcDriver;
// ..
#Override
public String getJdbcDriver() {
return this.jdbcDriver;
}
// ..
}
which provides certain kind of information about my application e.g. which JDBC driver I want to use. However, I don't know how I can use it as I am bootstrapping my server. How can I get hands on the initialized ApplicationConfiguration object and what is the best place to do this with Spring.
My current ansatz is using a ServletContextListener but at this point I don't know how to access the configuration object either:
public class BootstrappingServerConfig implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
// ..
// How can I load my application configuration at this point
// or is there a better place to do that actually?
ApplicationConfiguration applicationConfiguration;
try {
SqlDatabaseBootstrapper.executeMigration(dataSource, applicationConfiguration);
} catch(Exception e) {
throw e;
}
}
// ..
}
What can I do here? Is it a good idea to use a #Configuration object like that in the ServletContextListener or would I do this kind of initialization somewhere else? If it's okay what I plan to do .. how can I get the configuration object?

EclipseLink + JPA Guice Persist and Redeployments

I have an infrastructure based on EclipseLink + JPA Guice Persist
When I redeploy the application always I have caching problems with caching Entitys and I have to reboot the server (Oracle Weblogic 11g) .This problem is treated in a this post: https://bugs.eclipse.org/bugs/show_bug.cgi?id=326552 But, maybe is not a bug ¿?¿? ...
I managed to solve the problem as follows :
Originally I have centralized everything in a GuiceModule:
1.Create the module JPAPersist
2.Binding of a Initializer class thas invokes the persistenceService.start()
public class MyGuiceModule implements Module {
#Override
public void configure(final Binder binder) {
Properties props = _dbConnectionPropertiesForPool();
JpaPersistModule jpaModule = new JpaPersistModule(persistenceUnit);
jpaModule.properties(props);
binder.install(jpaModule);
binder.bind(JPAInitializer.class).asEagerSingleton();
}
public class JPAInitializer {
#Inject
public JPAInitializer(final PersistService service) {
service.start();
}
}
Everything works fine .... but as I said when redeploy remain cached instances
HOW DO I HAVE SOLVED?
I changed the method JPAInitializer
public static class JPAInitializer {
private static PersistService _persistenceService = null;
#Inject
public JPAInitializer(final PersistService service) {
_persistenceService = service;
_persistenceService.start();
}
public static void stop() {
_persistenceService.stop();
}
}
I created a method stop () that stops the service ..but WTF! I have been forced to save the the injected Persistence service in a static variable :((
From the guice / listener filter that is the entrypoint invoke the stop when the application is undeployed (onContextDestroyed)
public void contextDestroyed(ServletContextEvent servletContextEvent) {
JPAInitializer.stop();
}
Now, when i redeploy there is no cache issue or problem, and there is no need to restart the server
It works this way, but I do not know if it's all right to create a static instance PesistenceService., so i'm trying to find another way to invoke the stop.....
Any suggestion?
Found solution .
Create a inteface to handle Guice Persistence Service :
interface MyPersistenceServiceHandler {
public void start();
public void stop();
}
This will be used into the main DB Guice Module :
binder.bind(MyPersistenceServiceHandler .class)
.to(JPAPersistenceServiceControl.class)
.in(Singleton.class);
static class JPAPersistenceServiceControl
implements MyPersistenceServiceHandler {
private final PersistService _service;
#Inject
public JPAPersistenceServiceControl(final PersistService service) {
_service = service;
}
#Override
public void start() {
if (_service == null) throw new IllegalStateException("NO persistence service available!");
_service.start();
}
#Override
public void stop() {
if (_service == null) throw new IllegalStateException("NO persistence service available!");
_service.stop();
}
}
Get the instance in the RESTEndoint/Guice filter through Guice Injector.
jpaServiceHandler = _myGuiceInjector.getInstance(MyPersistenceServiceHandler .class);
Start the service on contextInitialized : jpaServiceHandler.start();
Stop the service on contextDeproyed : jpaServiceHandler.stop();

Resources