Spring init and destroy methods - spring

package com.idol;
public class Auditorium {
Auditorium(){
}
public void turnOnLights() {
System.out.println("Lights are turned on");
}
public void turnOffLights(){
System.out.println("Lights are turned off");
}
}
For xml context I have:
<bean id="Auditorium" class="com.idol.Auditorium" init-method="turnOnLights" destroy-method="turnOffLights"/>
Testing:
ApplicationContext auditorium =
new ClassPathXmlApplicationContext("ApplicationContextVer6.xml");
auditorium.getBean("Auditorium");
I get:
Does only print "Lights are turned on" and doesn't print "Lights are turned off". I though that before destroying the bean it should invoke the destroy-method too, what am I missing or not getting? (I have no errors in log, just in case)
Thanks

Try it like this:
final ConfigurableApplicationContext auditorium =
new ClassPathXmlApplicationContext("ApplicationContextVer6.xml");
auditorium.getBean("Auditorium");
auditorium.close(); // thx Nathan
// auditorium.refresh() will also turn the lights off
// before turning them on again

You cannot observe destroy method working, because beans are available in Spring context all the time. When you close/destroy your application context, then all beans instantiated within it should be destroyed. Take a look at the close() and destroy() methods at the org.springframework.context.support.AbstractApplicationContext class.

Related

Do JASON internal actions work with Spring Autowire?

I am developing an application using JADE, JASON (Agent frameworks) and Spring Boot. Basically what I have is a JADE Container where Both Jade and Jason Agents are registered in. And Since I am using Spring, I tend to Autowire services. In that case I am in need to access some services, inside some of my Jason internal actions (which I custom wrote extending DefaultInternalAction class). which seems not working. I have the idea how to Autowire and how the Beans work. My doubt is whether those internal actions are in the spring context or not. I guess they are not. Thats why may be the Autowire thing is not working. Can someone please explain me about the real action inside the jade container and internal actions so that I can think differently about using Autowire inside jason internal actions.
As far as I know, internal actions is created by jason, not spring that is why you cant autowire services. Personnaly, I create factory and use it for getting instance of a service. Something like this:
public class SpringPluginFactory {
private static final SpringPluginFactory INSTANCE = new SpringPluginFactory();
private ApplicationContext applicationContext;
private SpringPluginFactory(){}
private <T> T createPlugin(Class<T> iface) {
if(applicationContext == null){
throw new IllegalStateException("applicationContext cannot be null");
}
try {
return applicationContext.getBean(iface);
} catch (Exception e) {
throw new RuntimeException("factory unable to construct instance of " + iface.getName());
}
}
public static <T> T getPlugin(Class<T> iface){
return INSTANCE.createPlugin(iface);
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
then I create bean in order to set aplicationContext:
#Bean
public SpringPluginFactory pluginFactory(ApplicationContext applicationContext){
SpringPluginFactory pluginFactory = SpringPluginFactory.INSTANCE;
pluginFactory.setApplicationContext(applicationContext);
return pluginFactory;
}
and use the factory in any behaviours or internal actions
SpringPluginFactory.getPlugin(YouService.class).doSomething();
Maybe it will help.

Event not working in Message Driven Bean

I'm trying to generate and handle an event when my MDB receives a message. Here is what I'm doing:
public class MDBBooks implements MessageListener {
#Inject
private Event<Update> messageReceived;
public MDBLibri() {
}
#Override
public void onMessage(Message message) {
System.out.println("Message received");
try {
Update u = message.getBody(Update.class);
messageReceived.fire(u);
if(u != null){
... stuff
}
} catch (JMSException ex) {
System.out.println("JMSException: " + ex.getMessage());
}
}
public void eventHandler(#Observes Update up) {
System.out.println("There was an update");
}
}
But it just does not work, the string "There was an update" it's not printed in the glassfish console. I can't really tell what's the problem, my textbook does it the same way pretty much. I'm assuming the event fires fine, but the event handler isn't notified.
You are correct that the observer method does not get notified. In fact, CDI doesn't even know it exists. The reason is that in CDI, message-driven beans are non-contextual objects. To simplify, they are not considered CDI beans, but you can still inject into them and intercept them.
Now, for CDI to recognize an observer method, you have to place it in a managed bean or a session bean. Quoting the spec:
An observer method is a non-abstract method of a managed bean class or session bean class (or of an extension, as defined in Container lifecycle events).
So a solution for you would be to place your observer method in another class, which is either a managed bean or a session bean.

Adding Spring bean asynchronously

In my Grails 3.1.14 I app need to add a bean, created in a callback of the asynchronous method call:
Vertx.clusteredVertx( [:] ){ AsyncResult<Vertx> res ->
if( res.succeeded() ){
Vertx vertx = res.result() // << this should be injected into the appContext
}
}
so that the instance can be autowired into other artefacts across the whole application.
What's the proper way to achieve this?
Shall I do it with StaticApplicationContext or would it break anything?
Another way would be to use a "container-bean" and set it's propery upon completion of the async method, but it's kind of ugly.
You can create a wrapper bean that configures its properties based on application context events, using InitializingBean interface.
class VertxWrapper implements InitializingBean {
Vertx vertx
void afterPropertiesSet() throws Exception {
Vertx.clusteredVertx( [:] ){ AsyncResult<Vertx> res ->
this.vertx = res.result() // << this should be injected into
}
}
}
}
And then simply access vertxWrapper.vertx from any bean that has vertxWrapper injected into it.

Can I programmatically add a qualifier to a bean?

I am registering transaction managers in my code, I would normally use annotation based configuration but as I don't know until runtime how many data sources (and hence transaction managers) there will be, I have to programmatically register these, as follows:
private final void registerTransactionManagerBean(final DataSource dataSource, ConfigurableApplicationContext context) {
String transactionManagerName = this.getName() + "-transactionManager";
context.getBeanFactory().registerSingleton(transactionManagerName, new DataSourceTransactionManager(dataSource));
LOG.info("Registering transaction manager under name : " + transactionManagerName);
}
Assuming this.getName() returned 'mydb', I originally expected to be able to qualify a transaction manager like this:
#Transactional("mydb-transactionManager")
What I've realised however is the value of that annotation refers to the qualifier and not the name. I did a quick test by declaring a bean as below and it works:
#Bean
#Qualifier("mydb-transactionManager")
public PlatformTransactionManager test() {
return new DataSourceTransactionManager(new EmbeddedDatabaseBuilder().build());
}
My question is, is there a way I can programmatically add a qualifier when registering a bean?
UPDATE
I've worked this out, I'm falling foul of this problem (in BeanFactoryAnnotationUtils:isQualifierMatch):
catch (NoSuchBeanDefinitionException ex) {
// ignore - can't compare qualifiers for a manually registered singleton object
}
I am manually registering my transaction manager bean so I presume this is why I'm stuck. I'm not really sure what options that gives me apart from to not programmatically register transaction managers as a runtime thing sadly.
I've worked this out, I'm falling foul of this problem:
catch (NoSuchBeanDefinitionException ex) {
// ignore - can't compare qualifiers for a manually registered singleton object
}
I am manually registering my transaction manager bean so I presume this is why I'm stuck. I'm not really sure what options that gives me apart from to not programatically register transaction managers as a runtime thing sadly.
Raised as a JIRA issue - https://jira.spring.io/browse/SPR-11915
public class RuntimeRegistrationWithQualifierTest {
private AnnotationConfigApplicationContext context;
#Test
public void beanWithQualifier() {
final GenericBeanDefinition helloBeanDefinition = new GenericBeanDefinition();
helloBeanDefinition.addQualifier(new AutowireCandidateQualifier(Hello.class));
final GenericBeanDefinition worldBeanDefinition = new GenericBeanDefinition();
worldBeanDefinition.addQualifier(new AutowireCandidateQualifier(World.class));
final DefaultListableBeanFactory factory = context.getDefaultListableBeanFactory();
factory.registerBeanDefinition("helloBean", helloBeanDefinition);
factory.registerSingleton("helloBean", "hello");
factory.registerBeanDefinition("worldBean", worldBeanDefinition);
factory.registerSingleton("worldBean", "world");
context.register(Foo.class);
context.refresh();
final Foo foo = context.getBean(Foo.class);
assertThat(foo.hello).isEqualTo("hello");
assertThat(foo.world).isEqualTo("world");
}
#Before
public void newContext() {
context = new AnnotationConfigApplicationContext();
}
#Qualifier
#Retention(RUNTIME)
#Target({FIELD, PARAMETER})
#interface Hello {}
#Qualifier
#Retention(RUNTIME)
#Target({FIELD, PARAMETER})
#interface World {}
static class Foo {
final String hello;
final String world;
Foo(#Hello final String hello, #World final String world) {
this.hello = hello;
this.world = world;
}
}
}

what the usage of method start() in AbstractApplicationContext

I'm a spring user. and I start to read the source code of spring.
when I read AbstractApplicationContext, I found there's one method start(), I found that the method doesn't be called when ApplicationContext is initialized.
My questions:
1)what the usage of the method? according to the word's(start) meaning, I think it should be called before the ApplicationContext can work. but it doesn't.
2)how can I listen the event which applicationContext starting working? after reading the code, I found the method will publish ContextStartedEvent. but if I just initialize the context, the context still can work and don't publish event.I can't listen the event to track the start of applicationcontext.
The start method is part of the Lifecycle interface, which is called as part of the application startup process.
If you want to be notified when the context is starting you should declare a bean that implements the Lifecycle interface.
public class org.example.MyLifecycle implements Lifecycle {
private boolean started = false;
public boolean isRunning() {
return started;
}
public void start() {
System.err.println("MyLifecycle starting");
started = true;
}
public void stop() {
System.err.println("MyLifecycle stopping");
started = false;
}
}
Then
<bean class="org.example.MyLifecycle"/>
This is all handled, by default, by DefaultLifecycleProcessor unless there's a bean in the context called lifecycleProcessor which implements the LifecycleProcessor interface

Resources