We have the below scheduler defined in spring context xml file.
<!-- Item Scheduling -->
<task:scheduler id="itemScheduler" pool-size="1"/>
<task:scheduled-tasks scheduler="itemScheduler" >
<task:scheduled ref="itemQueuePoller" method="poll" fixed-delay="10000"/>
</task:scheduled-tasks>
and below is the java code for the execution
#Service("itemQueuePoller")
public class ItemQueuePoller {
private final Logger LOG = getLogger(ItemQueuePoller.class);
private ItemQueueDao itemQueueDao;
private ItemHandler itemHandler;
#Autowired
public ItemQueuePoller(#Qualifier("itemQueueDao") ItemQueueDao itemQueueDao,
#Qualifier("itemHandler") ItemHandler itemHandler) {
this.itemQueueDao= itemQueueDao;
this.itemHandler= itemHandler;
}
//scheduled via batch application context
public void poll() {
try {
List<ItemQueueEntry> entries = itemQueueDao.findNextBatch();
if (entries == null || entries.isEmpty()) return;
itemHandler.processJob(entries);
} catch (Exception e) {
LOG.error("Exception occurred while processing Queue Items due to: ", e);
throw new RuntimeException(e);
}
}
}
This was working fine everyday but one day it ran only two times and then stopped automatically without any exception in logs.
When we have restarted the application, it starts working fine.
My question is that why it was stopped automatically?
Related
I am using existing applicationContext.xml that is being used for our existing web application to create a new console based java process.
This process or accounting server is to process tasks of different companies (lets say 2 different companies at a time). For this ThreadPoolTaskExecutor gives a guarantee that no more than the defined limit threads will be executed.
In below snippet when I try to get the "taskExecutor" from application context it is throwing exception that no such bean exists
is it the correct way to register a component ? as am not getting the thread pool bean from application context, is it properly registered with application context ?
public static void main(String[] args) {
LOGGER.info("Starting Accounting Server ...");
LOGGER.info("initializing applicationContext ... from " + APPLICATION_CONTEXT_XML_FULL_PATH);
try (AbstractApplicationContext applicationContext = new FileSystemXmlApplicationContext("file:" + APPLICATION_CONTEXT_XML_FULL_PATH)) {
LOGGER.info("... applicationContext initialized");
ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();
threadPool.setCorePoolSize(MAX_RUNNING_THREADS);
threadPool.setMaxPoolSize(MAX_RUNNING_THREADS);
threadPool.setThreadNamePrefix("T--ACCOUNTING-THREAD--");
threadPool.initialize();
Object obj = applicationContext.getBeanFactory().initializeBean(threadPool, "taskExecutor");
// obj is returned
obj = applicationContext.getBean("taskExecutor"); // <--------- throws exception with no such bean
AccountingQueueTransacitonService accountingQueueTransacitonService = applicationContext.getBean(AccountingQueueTransacitonService.class);
/*
while (hastasks) {
// TODO:
// get tasks
// add to thread and push to thread pool
// get some sleep
// update hastasks flag
}
*/
} catch (Exception e) {
LOGGER.error(e, e);
} finally {
LOGGER.info("closed application context ...");
}
LOGGER.info("Ending Accounting Server gracefully ...");
}
I have to run a few methods when Application starts, like the following:
#SpringBootApplication
public class Application implements CommandLineRunner {
private final MonitoringService monitoringService;
private final QrReaderServer qrReaderServer;
#Override
public void run(String... args) {
monitoringService.launchMonitoring();
qrReaderServer.launchServer();
}
However, only the first one is executed! And the application is started:
... Started Application in 5.21 seconds (JVM running for 6.336)
... START_MONITORING for folder: D:\results
The second one is always skipped!
If change the call order - the only the second one will be executed.
Could not find any solution for launching both at the beginning - tried #PostConstruct, ApplicationRunner, #EventListener(ApplicationReadyEvent.class)...
Looks like they are blocking each other somehow. Despite the fact that both have void type.
Monitoring launch implementation:
#Override
public void launchMonitoring() {
log.info("START_MONITORING for folder: {}", monitoringProperties.getFolder());
try {
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind == ENTRY_CREATE) {
log.info("FILE_CREATED: {}", event.context());
// some delay for fully file upload
Thread.sleep(monitoringProperties.getFrequency());
String fullFileName = getFileName(event);
String fileName = FilenameUtils.removeExtension(fullFileName);
processResource(fullFileName, fileName);
}
}
key.reset();
}
} catch (InterruptedException e) {
log.error("interrupted exception for monitoring service", e);
} catch (IOException e) {
log.error("io exception while processing file", e);
}
}
QR Reader start (launch TCP server with Netty configuration):
#Override
public void launchServer() {
try {
ChannelFuture serverChannelFuture = serverBootstrap.bind(hostAddress).sync();
log.info("Server is STARTED : port {}", hostAddress.getPort());
serverChannel = serverChannelFuture.channel().closeFuture().sync().channel();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
shutdownQuietly();
}
}
How to solve this issue?
Start launchMonitoring() asynchronously.
The easiest way to do this is to enable Async by adding #EnableAsync on your Application
and then annotate launchMonitoring() with #Async
Not sure if launchServer() should also be started asynchronously.
EDIT: completed Answer
No task executor bean found for async processing: no bean of type TaskExecutor and no bean named 'taskExecutor' either
By default Spring will create a SimpleAsyncTaskExecutor, but you can provide your taskExecutor
Example:
#EnableAsync
#Configuration
public class AsyncConfig implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.set... // your custom configs
executor.initialize();
return executor;
}
...
}
I have created spring boot thread pool project which has thread that needs to run 24x7 once spawned but when I need to stop the app in server for some maintenance it should shutdown after completing its current task and not taking up any new task.
My code for the same is:
Config class
#Configuration
public class ThreadConfig {
#Bean
public ThreadPoolTaskExecutor taskExecutor(){
ThreadPoolTaskExecutor executorPool = new ThreadPoolTaskExecutor();
executorPool.setCorePoolSize(10);
executorPool.setMaxPoolSize(20);
executorPool.setQueueCapacity(10);
executorPool.setWaitForTasksToCompleteOnShutdown(true);
executorPool.setAwaitTerminationSeconds(60);
executorPool.initialize();
return executorPool;
}
}
Runnable class
#Component
#Scope("prototype")
public class DataMigration implements Runnable {
String name;
private boolean run=true;
public DataMigration(String name) {
this.name = name;
}
#Override
public void run() {
while(run){
System.out.println(Thread.currentThread().getName()+" Start Thread = "+name);
processCommand();
System.out.println(Thread.currentThread().getName()+" End Thread = "+name);
if(Thread.currentThread().isInterrupted()){
System.out.println("Thread Is Interrupted");
break;
}
}
}
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void shutdown(){
this.run = false;
}
}
Main class:
#SpringBootApplication
public class DataMigrationPocApplication implements CommandLineRunner{
#Autowired
private ThreadPoolTaskExecutor taskExecutor;
public static void main(String[] args) {
SpringApplication.run(DataMigrationPocApplication.class, args);
}
#Override
public void run(String... arg0) throws Exception {
for(int i = 1; i<=20 ; i++){
taskExecutor.execute(new DataMigration("Task " + i));
}
for (;;) {
int count = taskExecutor.getActiveCount();
System.out.println("Active Threads : " + count);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 0) {
taskExecutor.shutdown();
break;
}
}
System.out.println("Finished all threads");
}
}
I need help to understand if I need to stop my spring boot application it should stop all the 20 threads running which runs (24x7) otherwise after completing there current loop in while loop and exit.
I would propose couple of changes in this code to resolve the problem
1) since in your POC processCommand calls Thread.sleep, when you shutdown the executor and it interrupts workers InterruptedException get called but is almost ignored in your code. After that there is if(Thread.currentThread().isInterrupted()) check which will return false for the reason above. Similar problem is outlined in the post below
how does thread.interrupt() sets the flag?
the following code change should fix the problem:
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
shutdown();
}
}
2) Also because of ThreadConfig::taskExecutor executorPool.setWaitForTasksToCompleteOnShutdown(true) Spring will call executor.shutdown instead of executor.shutdownNow. According to javadoc ExecutorService.shutdown
Initiates an orderly shutdown in which previously submitted tasks are
executed, but no new tasks will be accepted.
So I would recommend to set
executorPool.setWaitForTasksToCompleteOnShutdown(false);
Other things to improve in this code: although DataMigration is annotated as a component the instances of this class are creared not by Spring. You should try using factory method similar to ThreadConfig::taskExecutor in order to make Spring initiate instances of DataMigration for example to inject other bean into DataMigration instances.
In order to shutdown executor when running jar file on linux environment you can for example add actuator module and enable shutdown endpoint:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
in application.properties:
endpoints.shutdown.enabled=true
It will enable JMX shutdown endpoint and you can call shutdown on it.
If you want current job cycle of the task to be finished you should set
executorPool.setWaitForTasksToCompleteOnShutdown(true);
In order to connect to your jvm process on linux env remotely you have to specify an RMI Registry port.
Here is a detailed article:
How to access Spring-boot JMX remotely
If you just need to connect to JMX from local env you can run jsoncole or command-line tools : Calling JMX MBean method from a shell script
Here is an example uf using one of these tools - jmxterm
$>run -d org.springframework.boot: -b org.springframework.boot:name=shutdownEndpoint,type=Endpoint shutdown
#calling operation shutdown of mbean org.springframework.boot:name=shutdownEndpoint,type=Endpoint with params []
#operation returns:
{
message = Shutting down, bye...;
}
I have a requirement where I do some operation on GUI and once I save the data in DB I need to send a http request to a webservice. But the response to GUI should not wait for result from webservice request.
For this I am using #Async , annotation of Spring.
here is my structure
MyConroller.java
calls a method
goSaveAndCreate
(not Async) in
ServiceA.java
ServiceA has a ServiceB bean injected in it. A method ,
#Async
create()
in ServiceB is annotated with Async.
Now ServiceA.goSaveAndCreate calls a method in itself , save and calls ServiceB.create() (which is Async).
I can see in logs the a new thread is created which is executing create method. But all of a sudden logs after a particular point stop and that thread seems to have got killed or comlpeted.
#Service("MarginCalculationService")
public class ServiceA implements ServiceAI {
private static final String APPROVED = "APPROVED";
public static final String SUCCESS = "SUCCESS";
....
#Autowired
ServiceB serviceB;
public List<VV> goSaveAndCreate(String[] ids,List<XX> calList) throws Exception, DataAccessException {
try {
Pair<List<VG>,List<UU>> resultList = save(ids);
vvList = resultList.getFirst();
/*
* HKAPIL - Send Message to webService callingserviceB
*/
if(resultList.getSecond() != null){
serviceB.create(resultList.getSecond());
}
} catch (DataAccessException e) {
e.printStackTrace();
logger.error("Data Access Exception thrown during - " , e);
throw e;
} catch (Exception e){
e.printStackTrace();
logger.error("Exception thrown during " , e);
throw e;
}
System.out.println("Exiting the method in MCSERVICE");
return vvList;
}
private save(){
...
...
}
}
Second service
#Service("wireInstructionMessageService")
public class ServiceB implements ServiceBI {
#Async
#Override
public void create(List<Ralc> calcList) {
String threadName = Thread.currentThread().getName();
logger.info("Inside a different thread [" + threadName + " ] to send message ." );
..
...
otherMethod(Obj);
}
private otherMethod(Obj obj){
...
...
..
//tills this point logs are getting printed nothing after this
..
...
}
}
applciationContext.xml entry
<!-- Defines a ThreadPoolTaskExecutor instance with configurable pool size, queue-capacity, keep-alive,
and rejection-policy values. The id becomes the default thread name prefix -->
<task:executor id="MyMessageExecutor"
pool-size="5-25"
queue-capacity="100"/>
<task:annotation-driven executor="MyMessageExecutor"/>
Now I have two question
1) is there a way I can add some logs in some method which tell taht the new thread from MyExecutor is getting killed or MyExecutor is getting closed (the way we have in normal Java ExecutorSerrvice)
2) Am I using the Asyn in wrong way? Is it possible that as soon as method returns from Controller or ServiceA , ServiceB instance also is getting cleaned?
Thanks
HKapil
I'm new to J2EE - MDB but I'm trying to create a Message Driven Bean (MDB) that simply listens to a queue (read the messages), then process that message and push it to a different queue. I have found several working examples on Google to achieve these two tasks in a separate fashion, but I've been having issues trying to do them both on the same MDB.
This is the code for my MDB
#MessageDriven(mappedName = "jms/propuestasQ")
public class ObtenerNumPolizaBean implements MessageListener {
#Resource(name="jms/polizasQCF")
private QueueConnectionFactory connectionFactory;
private Connection connection;
#Resource(name = "jms/polizasQ")
private Destination targetQueue;
#PostConstruct
private void initJMS() {
try {
connection = connectionFactory.createConnection();
} catch (JMSException e) {
throw new RuntimeException(e);
}
}
#PreDestroy
private void closeJMS() {
try {
connection.close();
} catch (JMSException e) {
throw new RuntimeException(e);
}
}
/**
* #see MessageListener#onMessage(Message)
*/
#Override
public void onMessage(Message message) {
//validate the received message type
if (message instanceof FolioEntity) {
try {
//generate Web Service proxy
GenerarFoliosImplService serviceGenerarFolios = new GenerarFoliosImplService();
GenerarFoliosImplDelegate delGenerarFolios = serviceGenerarFolios.getGenerarFoliosImplPort();
//call the method with the object
FolioEntity responseFolio = delGenerarFolios.generarFolios((FolioEntity)message);
System.out.println("Bean generated the following FolioNumber: " + responseFolio.getNumeroFolio());
//put the message on the next queue
putMessage(responseFolio);
}
catch (JMSException e) {
throw new RuntimeException(e);
}
}
else {
throw new IllegalArgumentException("Message must be of type FolioEntity");
}
}
private void putMessage(final FolioEntity folio) throws JMSException {
final Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
final MessageProducer producer = session.createProducer(targetQueue);
final ObjectMessage objectMessage = session.createObjectMessage();
producer.send(objectMessage);
session.close();
}
Here is the content of my ejb-jar.xml file
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
<display-name>MDBRenovarPolizaEJB </display-name>
<enterprise-beans >
<message-driven>
<ejb-name>ObtenerNumPolizaBean</ejb-name>
<message-destination-ref>
<description />
<message-destination-ref-name>
jms/polizasQ
</message-destination-ref-name>
<message-destination-type>
javax.jms.Queue
</message-destination-type>
<message-destination-usage>
ConsumesProduces
</message-destination-usage>
<message-destination-link>
jms/polizasQ
</message-destination-link>
</message-destination-ref>
<message-destination-ref>
<description />
<message-destination-ref-name>
jms/polizasQCF
</message-destination-ref-name>
<message-destination-type>
javax.jms.QueueConnectionFactory
</message-destination-type>
<message-destination-usage>
ConsumesProduces
</message-destination-usage>
<message-destination-link>
jms/polizasQCF
</message-destination-link>
</message-destination-ref>
The issue I'm having is that I can't set the "Message Driven Bean listener bindings" on WAS Console 8.5.5, when I try to set the activation specification I'm getting the error:
MDBRenovarPolizaModelEJB.jar\META-INF\ejb-jar_merged.xml (The system cannot find the file specified.)
I don't know what this exception means. I've always set the "Activation specification" this way to listen to a particular queue, so I have no idea what is this file: "ejb-jar_merged.xml".
Any clue? Thanks in advance.
Or if anyone has a working example to achieve this with step by step to make it work under WebSphere that would be useful.
I just solved similar problem (WAS 8.5.5, but MDB, EJB, servlet - all stuffed into one war module)
It is clearly a bug in WAS. This is workaround:
Ensure Run server with resources on Server
Publish to repeat an error (but this time with resources on server)
Find directory where WAS expects "ejb-jar_merged.xml":
Locate WAS SystemErr.log
There are messages about missing "ejb-jar_merged.xml".
You are looking for directory name of missing file.
Goto to the found directory.
(something like .IBM/WebSphere/AppServer/profiles/AppSrv01/wstemp/0/workspace/... ....deployments /.....-INF/
copy ejb-jar.xml ejb-jar_merged.xml
In case of missing web_merged.xml just copy web.xml into web_merged.xml.
The problem will not appear again at application updates, it has to be reapplied sometimes after app remove/install.