I am trying to implement an event framework using spring events.I came to know that the default behavior of spring event framework is sync. But during spring context initialization if it finds a bean with id applicationEventMulticaster it behaves Async.
Now i want to have both sync and async event publishers in my application, because some of the events needs to be published sync. I tried to configure sync event multicaster using SysncTaskExecutor, but i cant find a way to inject it into my AsyncEventPublisher's applicationEventPublisher property.
My spring configuration file is as below
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" destroy-method="shutdown">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
<property name="WaitForTasksToCompleteOnShutdown" value="true" />
</bean>
<bean id="syncTaskExecutor" class="org.springframework.core.task.SyncTaskExecutor" />
<bean id="customEventPublisher" class="x.spring.event.CustomEventPublisher" />
<bean id="customEventHandler" class="x.spring.event.CustomEventHandler" />
<bean id="eventSource" class="x.spring.event.EventSource" />
<bean id="responseHandler" class="x.spring.event.ResponseHandler" />
<bean id="syncEventSource" class="x.spring.event.syncEventSource" />
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="taskExecutor" />
</bean>
<bean id="syncApplicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="syncTaskExecutor" />
</bean>
Can anyone help me out here ?
I just had to work this out for myself. By default events are sent asynchronously except if you implement a marker interface, in my case I called it SynchronousEvent. You'll need an 'executor' in your config too (I omitted mine as it's quite customised).
#EnableAsync
#SpringBootConfiguration
public class BigFishConfig {
#Autowired AsyncTaskExecutor executor;
#Bean
public ApplicationEventMulticaster applicationEventMulticaster() {
log.debug("creating multicaster");
return new SimpleApplicationEventMulticaster() {
#Override
public void multicastEvent(final ApplicationEvent event, #Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : ResolvableType.forInstance(event);
if (event instanceof PayloadApplicationEvent
&& ((PayloadApplicationEvent<?>) event).getPayload() instanceof SynchronousEvent)
getApplicationListeners(event, type).forEach(l -> invokeListener(l, event));
else
getApplicationListeners(event, type).forEach(l -> executor.execute(() -> invokeListener(l, event)));
}
};
}
...
no, you can't do that, the spring initApplicationEventMulticaster just init only one, and the BeanName must be applicationEventMulticaster. so you just can choose one of below Executor:
- org.springframework.core.task.SyncTaskExecutor
- org.springframework.core.task.SimpleAsyncTaskExecutor
- your own Executor: org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
any way, you can modify org.springframework.context.event.SimpleApplicationEventMulticaster
to add your logic, then you can control whether need to Sync/Async
/**
* Initialize the ApplicationEventMulticaster.
* Uses SimpleApplicationEventMulticaster if none defined in the context.
* #see org.springframework.context.event.SimpleApplicationEventMulticaster
*/
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
i am not good for edit with stackoverflow. please forgive me.
SyncTaskExecutor
I don't need to add comment that you can know well. this is synchronized. this Executor run task in sequence, and blocked for every task.
public class SyncTaskExecutor implements TaskExecutor, Serializable {
/**
* Executes the given {#code task} synchronously, through direct
* invocation of it's {#link Runnable#run() run()} method.
* #throws IllegalArgumentException if the given {#code task} is {#code null}
*/
#Override
public void execute(Runnable task) {
Assert.notNull(task, "Runnable must not be null");
task.run();
}
}
SimpleAsyncTaskExecutor
This class is very large, so i just choose section of code. If you give threadFactory, will be retrieved Thread from this factory, or will be create new Thread.
protected void doExecute(Runnable task) {
Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
thread.start();
}
ThreadPoolTaskExecutor
this class use jdk5's current pkg ThreadPoolTaskExecutor. but spring encapsulate functionality. Spring is good at this way, jdk6's current and jdk7'scurrent pkg have some difference.
this will be get Thread from ThreadPool and reuse it, execute every task Asynchronized. If you want to know more detail, see JKD source code.
I tried below tutorial :
https://www.keyup.eu/en/blog/101-synchronous-and-asynchronous-spring-events-in-one-application
It helps in making sync and async multicaster and creates a wrapper over these. Make sure the name of the wrapper class (DistributiveEventMulticaster) is applicationEventMulticaster
Related
I am new to Spring-AOP and trying to use it in my project. I have created a class implementing MethodBeforeAdvice :
public class LogBeforeCallAdvice implements MethodBeforeAdvice{
/* (non-Javadoc)
* #see org.springframework.aop.MethodBeforeAdvice#before(java.lang.reflect.Method, java.lang.Object[], java.lang.Object)
*/
#Override
public void before(Method arg0, Object[] arg1, Object arg2)
throws Throwable {
System.out.println("**CALLING METHOD - " + arg0.getName() + " IN BEFORE CALLING ADVICE**");
}
}
These are the entries my application-context.xml file:
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="logMethods"></property>
<property name="interceptorNames">
<list>
<value>beforeCall</value>
</list>
</property>
</bean>
<bean id = "logMethods" class = "com.MyPackage.LogMethods" lazy-init = "true" init-method="init">
<property name = "someProperty" ref = "someBeanReference"/>
</bean>
And in my main method, I am getting the proxy like this:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
LogMethods logMethods = (LogMethods)context.getBean("proxy");
After executing the above code, I am not getting the log written in before() method of Before Calling Advice. But I am able to see the sys outs mentioned in the LogMethods class and its property reference beans also.
I want to print the before advice logs for all the methods in LogMethods and its property beans' methods (without using aspectj annotations).
I am not getting any error also. What am I doing wrong? Can someone please help?
Thanks in Advance!!
I need to implement Multi Threaded background process. My project is spring , hibernate based I tried
with below code which uses org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor to
perform the below background operation in multi threaded manner.I need to know why my
thread count always 1 ?
public class UserUpdateProcessor implements InitializingBean {
private ThreadPoolTaskExecutor executor;
public void afterPropertiesSet() throws Exception {
for(int i = 0; i < 10) //added this like after the 1st reply
executor.execute(new UserBackgorundRunner ());
}
}
private class UserBackgorundRunner extends Thread {
public UserBackgorundRunner() {
this.setDaemon(true);
this.setPriority(MIN_PRIORITY);
}
public void run() {
List<User> users = getUserList();;
for (User user : users) {
try {
log.debug("Active count :::::::::::::::::::::::::::::"+executor.getActiveCount());
upgradeUserInBackground(user);
} catch (Exception e) {
LOGGER.warn("Fail to upgrade user");
}
}
}
My spring.xml looks like
<bean id="userThreadPool"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize"><value>10</value></property>
<property name="maxPoolSize"><value>15</value></property>
<property name="queueCapacity"><value>50</value></property>
</bean>
<bean id="userProcessor" class="com.user.UserUpdateProcessor"
autowire="byType">
<property name="executor" ref="userThreadPool" />
</bean>
It is always one because you only ever submit a single Thread to the ThreadPoolTaskExecutor.
Spring's InitializingBean (JavaDoc link) method afterPropertiesSet() is only invoked once in the Applications lifetime, and as far as I can tell from the example you have provided, that is the only thing submitting Thread's to your ThreadPoolTaskExecutor.
I have to asynchronously push some files in from my system A to system B. For that i have created a JMS Consumer. Once Entries are made in queue successfully using an enqueue stored procedure in oracle. My consumer should read the message and send it to system B.
Here is my Listeners Code
public class DMSCustomMessageListener extends DefaultMessageListenerContainer{
protected MessageConsumer createConsumer(Session session, Destination destination)
throws JMSException
{
return ((AQjmsSession)session).createConsumer(destination,
getMessageSelector(),
DMS_Master_Type.getORADataFactory(), null, isPubSubNoLocal());
}
}
public class DMSListener implements FactoryBean{
private ConnectionFactory connectionFactory;
private String queueName;
private String queueUser;
#Required
public void setConnectionFactory(QueueConnectionFactory connectionFactory)
{
System.out.println("set connection");
this.connectionFactory = connectionFactory;
}
#Required
public void setQueueName(String queueName) {
System.out.println("set DMS listener queuename");
this.queueName = queueName;
}
#Required
public void setQueueUser(String queueUser) {
System.out.println("set DMS listener queueuser");
this.queueUser = queueUser;
}
public Object getObject() throws Exception
{
QueueConnectionFactory qconn = (QueueConnectionFactory)this.connectionFactory;
AQjmsSession session = (AQjmsSession)qconn.createQueueConnection("score", "score").createQueueSession(true, 0);
return session.getQueue(this.queueUser, this.queueName);
}
public Class getObjectType()
{
return Queue.class;
}
public boolean isSingleton() {
return false;
}
}
Here is how i configured it.
<bean id="messageDMSListener" class="com.test.DMSTextListener">
</bean>
<bean id="testDMS" class="com.test.DMSListener">
<property name="connectionFactory" ref="aqConnectionFactoryRspm"/>
<property name="queueName" value="RSPM_PEND_REQ_Q_DMS"/>
<property name="queueUser" value="score"/>
</bean>
<bean id="jmsDMSContainer" class="com.test.DMSCustomMessageListener">
<property name="connectionFactory" ref="aqConnectionFactoryRspm"/>
<property name="destination" ref="testDMS"/>
<property name="messageListener" ref="messageDMSListener" />
<property name="sessionTransacted" value="true"/>
<property name="errorHandler" ref="listenerErrorHandler"/>
</bean>
In my queue table/view (AQ$RSPM_PEND_REQ_Q_DMS) i am gettting expiration reason as 'MAX_RETRY_EXCEEDED' . I have configured it to 10.
What can be the possible reason ? Kindly help.
Oracle Database Queue System differs from the common JMS system so does the way talk to it.
I assume you can talk with your queue but the messages do not disappear form the queue but expire instead. If that's the case then I think your queue is configured as "multi user" type. In such occasion it won't disappear until all recipients get the message and the queue owner is also the recipient. As you just want to pass the message to another system reconfigure your queue to single user and the message disappear immediately after reading.
As a matter of fact you don't need your java bean either. You can do the job by configuring queue propagation(and the corresponding job) straight in the database without any external objects (example skeleton below is not complete solution):
BEGIN
DBMS_AQADM.SCHEDULE_PROPAGATION (
queue_name => 'init_queue',
destination => NULL,
start_time => SYSDATE,
duration => NULL,
next_time => NULL,
latency => 60,
destination_queue => 'dest_queue');
END;
I have problems with integration spring and quartz. I need to dynamically added CronTriggerFactoryBean to SchedulerFactoryBean.
XML Spring mapping:
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" scope="prototype">
<property name="jobDetails">
<list>
<ref bean="plannedVacationServiceJob" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="plannedVacationServiceCronTrigger" />
</list>
</property>
</bean>
<bean id="plannedVacationServiceJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.my.service.package.PlannerJob" />
<property name="durability" value="true" />
</bean>
<bean id="plannedVacationServiceCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean" scope="prototype">
<property name="jobDetail" ref="plannedVacationServiceJob" />
<property name="cronExpression" value="*/15 * * * * ?" />
</bean>
Java code:
#Service
public class Planner implements Planning {
#Autowired
#Qualifier("plannedVacationServiceCronTrigger")
private CronTriggerFactoryBean plannedVacationServiceCronTrigger;
#Autowired
private SchedulerFactoryBean schedulerFactoryBean;
#PostConstruct
public void init() {
schedulerFactoryBean.start();
}
//some code
private void addTask(PlannerEntity entity) {
try {
String name = getIdentityName(entity);
JobKey jobKey = new JobKey(name);
String cronExpression = getCronExpression(entity);
plannedVacationServiceCronTrigger.setCronExpression(cronExpression);
JobDetail jobDetail = JobBuilder.newJob(PlannerJob.class).withIdentity(jobKey).build();
plannedVacationServiceCronTrigger.setJobDetail(jobDetail);
plannedVacationServiceCronTrigger.setName(name);
plannedVacationServiceCronTrigger.afterPropertiesSet();
schedulerFactoryBean.getScheduler().scheduleJob(jobDetail, plannedVacationServiceCronTrigger.getObject());
triggers.put(entity.getId(), trigger);
} catch (Exception e) {
e.printStackTrace();
}
}
}
In this case we have only one trigger, which added in xml mapping
I trying remove reference to triggers from xml and creating CronTriggerFactoryBean from code:
private void addTask(PlannerEntity entity) {
try {
String name = getIdentityName(entity);
JobKey jobKey = new JobKey(name);
String cronExpression = getCronExpression(entity);
CronTriggerFactoryBean trigger = new CronTriggerFactoryBean();
trigger.setCronExpression(cronExpression);
JobDetail jobDetail = JobBuilder.newJob(PlannerJob.class).withIdentity(jobKey).build();
trigger.setJobDetail(jobDetail);
trigger.setName(name);
trigger.afterPropertiesSet();
schedulerFactoryBean.getScheduler().scheduleJob(jobDetail, trigger.getObject());
triggers.put(entity.getId(), trigger);
} catch (Exception e) {
e.printStackTrace();
}
}
But in this case we dont have any triggers:
2014-04-02 14:28:55,144 DEBUG [org.quartz.core.QuartzSchedulerThread(org.springframework.scheduling.quartz.SchedulerFactoryBean#0_QuartzSchedulerThread)] - batch acquisition of 0 triggers
2014-04-02 14:29:18,981 DEBUG [org.quartz.core.QuartzSchedulerThread(org.springframework.scheduling.quartz.SchedulerFactoryBean#0_QuartzSchedulerThread)] - batch acquisition of 0 triggers
2014-04-02 14:29:46,361 DEBUG [org.quartz.core.QuartzSchedulerThread(org.springframework.scheduling.quartz.SchedulerFactoryBean#0_QuartzSchedulerThread)] - batch acquisition of 0 triggers
2014-04-02 14:30:09,439 DEBUG [org.quartz.core.QuartzSchedulerThread(org.springframework.scheduling.quartz.SchedulerFactoryBean#0_QuartzSchedulerThread)] - batch acquisition of 0 triggers
You can't really do it that way. FactoryBean has a specific contract and you're trying to act in the middle of it.
A factory bean is a bean whose purpose is to create a bean programmatically. You can't really inject a factory bean because the sole purpose of its existence is to register a bean. Besides, the start callback is related to the application context lifecycle. When you call it from your code, it is too late, the scheduler is already active.
The triggers are registered when the bean is initialized (in the afterPropertiesSet callback method). If you try to add additional triggers to the factory after that, it will not be taken into account as the scheduler has already been created.
That being said, I don't really see the relationship between the XML and the Java code. In a regular scenario, your plannedVacationServiceCronTrigger should be registered (and there is no reason to make that a prototype bean).
Lest's consider that I have the following:
public class MyRunnable implements Runnable {
public void run() {
//do something expensive
}
}
public class ThreadExecutor {
private TaskExecutor taskExecutor;
public ThreadExecutor(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public void fireThread(){
taskExecutor.execute(new MyRunnable());
}
}
my xml is the following:
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
<property name="queueCapacity" value="25" />
</bean>
<bean id="threadExecutor" class="com.vanilla.threads.controllers.ThreadExecutor">
<constructor-arg ref="taskExecutor" />
</bean>
in my Spring MVC controller I start the task:
#RequestMapping(value="/startTask.html", method=RequestMethod.GET)
public ModelAndView indexView(){
ModelAndView mv = new ModelAndView("index");
threadExecutor.fireThread();
return mv;
}
Now let's consider that I would like to create another request(#RequestMapping(value="/checkStatus.html") which will tell if the the task started in my previous Request has been finished.
So my questions are simple:
1) Can I assign name to the task in TaskExecutor and if yes, how can I do it?
2) How can I check that the task with that specific name has been done?
1) No, but..
Instead of using taskExecutor.execute() use taskExecutor.submit() which returns a Future. Put the Future in the HttpSession, and when a checkStatus request comes in, pull the Future from the session and call isDone() on it. If you need to give it a name then instead of putting the Future in the session directly have a Map<String, Future> in the session where the key is the name of your task.