I like to run two tasks undependent from each other. The problem is, that one of the task has a blocking part. As a result the second task is waiting for the first task. Because of that, I think both tasks are running on the same thread. But I like to run each task on a different thread with the aim, that each task don't have to wait for each other.
I have a configuration class AppConfig:
#Configuration
#EnableAsync
#EnableScheduling
public class AppConfig
{...
There I have defined two beans:
#Bean(initMethod = "watchFtp")
public FTPWatchScheduledTask ftpWatchScheduledTaskService()
{
FTPWatchScheduledTask ftpWatchScheduledTask = new FTPWatchScheduledTaskService();
return ftpWatchScheduledTask;
}
and
#Bean(initMethod = "watch")
public FileWatch fileWatchService()
{
...
return fileWatchService;
}
Further the AppConfig is registered by the context:
public class FileSyncApplicationInitializer
implements WebApplicationInitializer
{
private static final Log log = LogFactory.getLog(FileSyncApplicationInitializer.class);
private static AnnotationConfigWebApplicationContext context;
#Override
public void onStartup(ServletContext container)
{
// Create the Spring application context
log.debug("Initializing file synchronisation ...");
context = new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
log.debug("Contexts initialized ...");
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
Both defined init-Methods of each bean are annotated with "#Async":
#Async
public void watch() throws IOException
{
WatchService watcher = FileSystems.getDefault().newWatchService();
WatchKey key = temporaryDir.register(watcher,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
for (; ; )
{
try
{
key = watcher.take(); // take is waiting
}
catch (InterruptedException e)
{
e.printStackTrace();
}
for (WatchEvent<?> pollEvent : key.pollEvents())
{
WatchEvent.Kind<?> kind = pollEvent.kind();
if (kind == StandardWatchEventKinds.ENTRY_CREATE)
{
WatchEvent<Path> event = (WatchEvent<Path>)pollEvent;
Path fileName = (Path)key.watchable();
Path fullPath = fileName.resolve(event.context());
orderImportService.persistOrderXml(fileName);
log.info("something was persisted");
}
}
boolean valid = key.reset();
if (!valid)
{
break;
}
}
}
Second init-method:
#Async
#Scheduled(fixedRate = 5000)
public void watchFtp()
{
log.info(String.format("test time: " + dateFormat.format(new Date())));
}
Related
I have the class below in which I want to configure my Camunda process engine.
I want to make the engine deployment-aware and set the backoff-time-in-millis.
I can do the former using config.setJobExecutorDeploymentAware(true);.
How can I set the backoff-time-in-millis and other parameters of the job executor?
The obvious solution (config.getJobExecutor().setBackoffTimeInMillis(100);) is not applicable because when the method org.example.Config#processEngineConfiguration is called, config.getJobExecutor() is equal to null.
#Configuration
#Import( SpringProcessEngineServicesConfiguration.class )
public class Config {
private static final Logger LOGGER = LoggerFactory.getLogger(Config.class);
#Autowired
#Qualifier("camundaBpmDataSource")
private DataSource dataSource;
#Autowired
#Qualifier("camundaTxManager")
private PlatformTransactionManager txManager;
#Autowired
private ResourcePatternResolver resourceLoader;
#Bean
public SpringProcessEngineConfiguration processEngineConfiguration() {
final SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();
config.setJobExecutorDeploymentAware(true);
config.setIdGenerator(new StrongUuidGenerator());
config.setDataSource(dataSource);
config.setTransactionManager(txManager);
config.setDatabaseSchemaUpdate("true");
config.getProcessEnginePlugins().add(new SpinProcessEnginePlugin());
config.setHistory(HistoryLevel.HISTORY_LEVEL_FULL.getName());
config.setJobExecutorActivate(true);
config.setMetricsEnabled(false);
final Logger logger = LoggerFactory.getLogger("History Event Handler");
final HistoryEventHandler testHistoryEventHandler = new HistoryEventHandler() {
#Override
public void handleEvent(final HistoryEvent evt) {
LOGGER.debug("handleEvent | " + evt.getProcessInstanceId() + " | "
+ evt.toString());
}
#Override
public void handleEvents(final List<HistoryEvent> events) {
for (final HistoryEvent curEvent : events) {
handleEvent(curEvent);
}
}
};
config.setHistoryEventHandler(new CompositeHistoryEventHandler(Collections.singletonList(testHistoryEventHandler)));
try {
final Resource[] bpmnResources = resourceLoader.getResources("classpath:*.bpmn");
final Resource[] dmnResources = resourceLoader.getResources("classpath:*.dmn");
config.setDeploymentResources(addAll(bpmnResources, dmnResources));
} catch (final IOException exception) {
exception.printStackTrace();
LOGGER.error("An error occurred while trying to deploy BPMN and DMN files", exception);
}
return config;
}
}
Add following code to the method processEngineConfiguration() above:
final ThreadPoolJobExecutor executor = new DefaultJobExecutor();
executor.setMaxJobsPerAcquisition(3);
executor.setWaitTimeInMillis(500);
executor.setLockTimeInMillis(300000);
executor.setMaxBackoff(150);
executor.setMaxWait(50);
final SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();
config.setJobExecutor(executor);
config.setJobExecutorDeploymentAware(true);
I have a spring boot application that uses the libraries: SimpleMessageListenerContainer (https://docs.spring.io/spring-amqp/docs/current/api/org/springframework/amqp/rabbit/listener/SimpleMessageListenerContainer.html) and SimpleMessageListenerContainerFactory (https://www.javadoc.io/static/org.springframework.cloud/spring-cloud-aws-messaging/2.2.0.RELEASE/org/springframework/cloud/aws/messaging/config/SimpleMessageListenerContainerFactory.html). The application uses ASW SQS and Kafka but I'm experiencing some out of order data and trying to investigate why. Is there a way to view logging from the libraries? I know I cannot edit them directly but when I create the bean, I want to be able to see the logs from those two libraries and if possible to add to them.
Currently I'm setting up the bean in this way:
#ConditionalOnProperty(value = "application.listener-mode", havingValue = "SQS")
#Component
public class SqsConsumer {
private final static Logger logger = LoggerFactory.getLogger(SqsConsumer.class);
#Autowired
private ConsumerMessageHandler consumerMessageHandler;
#Autowired
private KafkaProducer producer;
#PostConstruct
public void init() {
logger.info("Loading SQS Listener Bean");
}
#SqsListener("${application.aws-iot.sqs-url}")
public void receiveMessage(String message) {
byte[] decodedValue = Base64.getDecoder().decode(message);
consumerMessageHandler.handle(decodedValue, message);
}
#Bean
public SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory(AmazonSQSAsync amazonSqs) {
SimpleMessageListenerContainerFactory factory = new SimpleMessageListenerContainerFactory();
factory.setAmazonSqs(amazonSqs);
factory.setMaxNumberOfMessages(10);
factory.setWaitTimeOut(20);
logger.info("Created simpleMessageListenerContainerFactory");
logger.info(factory.toString());
return factory;
}
}
For reference, this is a method in the SimpleMessageListenerContainer. It is these logs which I would like to investigate and potentially add to:
#Override
public void run() {
while (isQueueRunning()) {
try {
ReceiveMessageResult receiveMessageResult = getAmazonSqs()
.receiveMessage(
this.queueAttributes.getReceiveMessageRequest());
CountDownLatch messageBatchLatch = new CountDownLatch(
receiveMessageResult.getMessages().size());
for (Message message : receiveMessageResult.getMessages()) {
if (isQueueRunning()) {
MessageExecutor messageExecutor = new MessageExecutor(
this.logicalQueueName, message, this.queueAttributes);
getTaskExecutor().execute(new SignalExecutingRunnable(
messageBatchLatch, messageExecutor));
}
else {
messageBatchLatch.countDown();
}
}
try {
messageBatchLatch.await();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
catch (Exception e) {
getLogger().warn(
"An Exception occurred while polling queue '{}'. The failing operation will be "
+ "retried in {} milliseconds",
this.logicalQueueName, getBackOffTime(), e);
try {
// noinspection BusyWait
Thread.sleep(getBackOffTime());
}
catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
}
SimpleMessageListenerContainer.this.scheduledFutureByQueue
.remove(this.logicalQueueName);
}
How would I be able to see all of that logging from where I create the bean?
Any help would be much appreciated!
I'm dwelling with Quartz from days..
I need to create, the app starts, some triggers and job details..
So, this is my Job
#DisallowConcurrentExecution
public class TimeoutJob extends QuartzJobBean{
public final String ID = "idInterruttore";
private final Logger logger = Logger.getLogger(TimeoutJob.class);
#Autowired InterruttoreService interruttoreService;
#Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
int idInterruttore = dataMap.getIntFromString(ID);
Interruttore interruttore = interruttoreService.findById(idInterruttore);
logger.debug("Job reached for " + interruttore.getNomeInterruttore());
}
}
Then i configure some bean in QuartzConfiguration.java
#Configuration
#ComponentScan("it.besmart")
public class QuartzConfiguration {
#Autowired
ApplicationContext applicationContext;
#Bean
public SchedulerFactoryBean scheduler() {
SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
schedulerFactory.setJobFactory(springBeanJobFactory());
return schedulerFactory;
}
#Bean
public SpringBeanJobFactory springBeanJobFactory() {
AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
}
Now, I have a JobManager.class which manage jobDetails and Triggers
#Service("jobManager")
public class JobManager {
private final Logger logger = Logger.getLogger(JobManager.class);
#Autowired
SchedulerFactoryBean scheduler;
#Autowired
InterruttoreService interruttoreService;
#PostConstruct
public void createInitialJobs() {
logger.debug("Start ut jobs to create");
List<Interruttore> interruttori = interruttoreService.findAllSwitches();
Date now = new Date();
for (int i = 0; i < interruttori.size(); i++) {
Interruttore interruttore = interruttori.get(i);
if (interruttore.getTimeoutDate().after(now) && interruttore.isStato()) {
// JobDetail and Trigger creation
createJob(interruttore, interruttore.getTimeoutDate());
}
}
}
public void createJob(Interruttore interruttore, Date richiesta) {
JobDetailFactoryBean jobDetail = new JobDetailFactoryBean();
jobDetail.setJobClass(TimeoutJob.class);
jobDetail.setName("Job detail for " + interruttore.getNomeInterruttore());
jobDetail.setDescription("Job Description");
jobDetail.setDurability(true);
Map<String, Integer> map = new HashMap<String,Integer>();
map.put("idInterruttore", interruttore.getIdInterruttore());
jobDetail.setJobDataAsMap(map);
long future = richiesta.getTime() - new Date().getTime();
logger.debug("next timeout is " + future / 1000 / 60 + " minuti for " + interruttore.getNomeInterruttore());
//trigger creation
SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean();
trigger.setName("myTrigger"+interruttore.getNomeInterruttore());
trigger.setGroup("timeoutTriggers");
trigger.setJobDetail(jobDetail.getObject());
trigger.setStartDelay(0);
trigger.setRepeatCount(1);
trigger.setRepeatInterval(future);
trigger.afterPropertiesSet();
logger.debug("Trigger for " + interruttore.getNomeInterruttore());
logger.debug("Trigger object is :" + trigger.getObject());
logger.debug("Next Trigger date " + trigger.getObject().getFinalFireTime());
try {
scheduler.getScheduler().scheduleJob(jobDetail.getObject(), trigger.getObject());
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
When launching the app, the #PostConstruct method tries to create the triggers, but i'm getting an exception when creating jobManager
Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jobManager': Invocation of init method failed; nested exception is java.lang.NullPointerException
caused by
Caused by: java.lang.NullPointerException
at org.springframework.scheduling.quartz.SimpleTriggerFactoryBean.afterPropertiesSet(SimpleTriggerFactoryBean.java:231)
at it.besmart.quartz.JobManager.createJob(JobManager.java:85)
at it.besmart.quartz.JobManager.createInitialJobs(JobManager.java:54)
which is
trigger.afterPropertiesSet();
as my triggers are not created...
There is a bug in the spring-context-support jar 4.2.5 version.
sti.setJobKey(this.jobDetail.getKey());
i.e. jobDetail can be null.
In the new versions it is fixed. I checked 4.3.2 version.
You can use 4.3.2 or later.
In 4.3.2 version
if (this.jobDetail != null) {
sti.setJobKey(this.jobDetail.getKey());
}
I am trying to deploy a HASingleton on a JBoss6.4. I have followed this tutorial to come up with the following:
I create a Service which is supposed to start a timer (own timer interface), by injecting the timer bean through JNDI.
public class HATimerService implements Service<String> {
private Logger logger = Logger.getLogger(HATimerService.class);
private final AtomicBoolean started = new AtomicBoolean(false);
private ServiceName serviceName;
private final InjectedValue<ServerEnvironment> env = new InjectedValue();
private String JNDI = "java:global/my-ear/my-module/MyTimer"
public HATimerService() {
serviceName = ServiceName.JBOSS.append(new String[]{"my", "ha", "singleton", "MyHaService"});
}
public String getValue() throws IllegalStateException, IllegalArgumentException {
return "";
}
public void start(StartContext context) throws StartException {
if(!started.compareAndSet(false, true)) {
throw new StartException("The service is still started!");
} else {
try {
InitialContext e = new InitialContext();
TimerScheduler myTimer = (TimerScheduler)e.lookup(JNDI);
timer.startTimer();
} catch (NamingException var6) {
throw new StartException("Could not initialize timer", var6);
}
}
}
public void stop(StopContext context) {
if(started.compareAndSet(true, false)) {
try {
InitialContext e = new InitialContext();
((TimerScheduler)e.lookup(JNDI)).stopTimer();
} catch (NamingException var4) {
logger.error("Could not stop timer", var4);
}
}
}
public ServiceName getServiceName() {
return serviceName;
}
public InjectedValue<ServerEnvironment> getEnvironment() {
return env;
}
}
I also have an activator which activates the service.
public class HATimerServiceActivator implements ServiceActivator {
private final Logger log = Logger.getLogger(this.getClass());
public HATimerServiceActivator() {
}
public void activate(ServiceActivatorContext context) {
HATimerService service = new HATimerService();
this.log.info(service.getServiceName() + "HATimerService will be installed");
SingletonService singleton = new SingletonService(service, service.getServiceName());
singleton.build(new DelegatingServiceContainer(context.getServiceTarget(), context.getServiceRegistry()))
.addDependency(ServerEnvironmentService.SERVICE_NAME, ServerEnvironment.class, service.getEnvironment())
.setInitialMode(Mode.ACTIVE)
.install();
}
}
The timer bean, HATimerService, and the HATimerServiceActivator are all deployed in an ear called my-ear. In the log files I can see:
JNDI bindings for session bean named MyTimer.... :
java:global/my-ear/my-module/MyTimer
However, every once in a while (approx. 1/3 of all deploys), this setup fails due to a NameNotFoundException where the JNDI lookup fails. The full exception is: Caused by: javax.naming.NameNotFoundException: Error looking up my-ear/my-module/MyTimer, service service jboss.naming.context.java.global.my-ear.my-module.MyTimer is not started
My guess is that this can be some sort of race condition where the bean isn't registered in the JNDI-tree yet. How can I make the service wait with the lookup until the bean is available?
It seems that there exists a possibility to create dependencies on deployment units. When creating the SingletonService, the following dependency can be added:
ServiceName ejbDependency = ServiceName.of("jboss", "deployment", "subunit", "my-ear.ear", "my-module.jar", "component", "MyTimerBean", "START");
singleton.build(new DelegatingServiceContainer(context.getServiceTarget(), context.getServiceRegistry()))
.addDependency(ServerEnvironmentService.SERVICE_NAME, ServerEnvironment.class, service.getEnvironment())
.setInitialMode(Mode.ACTIVE)
.addDependency(ejbDependency)
.install();
As long as ejbDependency is a correct dependency, the lookup will be performed after bean start.
In our REST-Service we want to implement a job that checks something every 10 seconds. So we thought we could use Quartz to make a Job that cover this. But the problem is, that we need to inject a singleton, because it is used in the job and the job seems to be not in the context of our service, so the injected class is always null (NullPointerException).
So is there another possible solution to achieve such a job without using Quartz? Already tried to write our own JobFactory that connects the job with the BeanManager, but it didnt work at all.
This is the code for the job that is not working:
#Stateless
public class GCEStatusJob implements Job, Serializable{
private Logger log = LoggerFactory.getLogger(GCEStatusJob.class);
#Inject
SharedMemory sharedMemory;
#Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
GoogleComputeEngineFactory googleComputeEngineFactory = new GoogleComputeEngineFactory();
List<HeartbeatModel> heartbeatList = new ArrayList<>(sharedMemory.getAllHeartbeats());
List<GCE> gceList = googleComputeEngineFactory.listGCEs();
List<String> ipAddressList = gceList.stream().map(GCE::getIp).collect(Collectors.toList());
for(HeartbeatModel heartbeat : heartbeatList){
if(ipAddressList.contains(heartbeat.getIpAddress())){
long systemTime = System.currentTimeMillis();
if(systemTime-heartbeat.getSystemTime()>10000){
log.info("Compute Engine mit IP "+heartbeat.getIpAddress()+" antwortet nicht mehr. Wird neu gestartet!");
String name = gceList.stream().filter((i) -> i.getIp().equals(heartbeat.getIpAddress())).findFirst().get().getName();
googleComputeEngineFactory.resetGCE(name);
}
}
}
}
}
SharedMemory is always null.
I have used Scheduler context map to achive this. You can try this.
In REST API when we create a Scheduler we can use the Context map to pass the parameters to Job
#Path("job")
public class RESTApi {
private String _userID;
public String get_userID() {
return _userID;
}
public void set_userID(String _userID) {
this._userID = _userID;
}
#GET
#Path("/start/{userId}")
public void startJob(#PathParam("userId") String userID) {
_userID = userID;
try {
SimpleTrigger trigger = new SimpleTrigger();
trigger.setName("updateTrigger");
trigger.setStartTime(new Date(System.currentTimeMillis() + 1000));
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
trigger.setRepeatInterval(1000);
JobDetail job = new JobDetail();
job.setName("updateJob");
job.setJobClass(GCEStatusJob.class);
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.getContext().put("apiClass", this);
scheduler.start();
scheduler.scheduleJob(job, trigger);
} catch (Exception e) {
e.printStackTrace();
}
}
}
JOB implementation
public class GCEStatusJob implements Job {
#Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
RESTApi apiClass;
try {
apiClass = ((RESTApi) arg0.getScheduler().getContext().get("apiClass"));
System.out.println("User name is" + apiClass.get_userID());
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
Correct me, if my understanding is wrong.