package ee.telekom.workflow.executor.producer;
import java.lang.invoke.MethodHandles;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import ee.telekom.workflow.core.common.WorkflowEngineConfiguration;
import ee.telekom.workflow.core.lock.LockService;
import ee.telekom.workflow.core.notification.ExceptionNotificationService;
import ee.telekom.workflow.core.workunit.WorkUnit;
import ee.telekom.workflow.core.workunit.WorkUnitService;
import ee.telekom.workflow.util.ExecutorServiceUtil;
import ee.telekom.workflow.util.NamedPoolThreadFactory;
@Component
public class WorkProducerJobImpl implements WorkProducerJob{
private static final Logger log = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() );
private static final int WORK_MAX_BATCH_SIZE = 1000;
@Autowired
private WorkProducerService workProducerService;
@Autowired
private WorkUnitService workUnitService;
@Autowired
private LockService lockService;
@Autowired
private WorkflowEngineConfiguration config;
@Autowired
private ExceptionNotificationService exceptionNotificationService;
private volatile AtomicBoolean isStarted = new AtomicBoolean( false );
private volatile AtomicBoolean isSuspended = new AtomicBoolean( false );
private ScheduledExecutorService scheduledExecutorService;
@Override
public synchronized void start(){
scheduledExecutorService = Executors.newScheduledThreadPool( 1, new NamedPoolThreadFactory( "producer" ) );
scheduledExecutorService.scheduleWithFixedDelay(
new ProducerRunnable(),
0,
config.getProducerIntervalSeconds(),
TimeUnit.SECONDS );
isSuspended.set( false );
isStarted.set( true );
log.info( "Started producer" );
}
@Override
public synchronized void stop(){
log.debug( "Stopping producer" );
ExecutorServiceUtil.shutDownSynchronously( scheduledExecutorService );
isSuspended.set( false );
isStarted.set( false );
log.info( "Stopped producer" );
}
@Override
public synchronized boolean isStarted(){
return isStarted.get();
}
@Override
public void suspend(){
isSuspended.set( true );
}
@Override
public void resume(){
isSuspended.set( false );
}
@Override
public boolean isSuspended(){
return isSuspended.get();
}
private class ProducerRunnable implements Runnable{
@Override
public void run(){
try{
if( isSuspended.get() || !lockService.refreshOwnLock() ){
return;
}
try{
List<WorkUnit> unprocessedWorkUnits = workUnitService.findNewWorkUnits( new Date() );
do {
workProducerService.produceWork( unprocessedWorkUnits, WORK_MAX_BATCH_SIZE );
} while ( !unprocessedWorkUnits.isEmpty() );
}
catch( Exception e ){
log.error( e.getMessage(), e );
exceptionNotificationService.handleException( e );
}
}
catch( Exception e ){
log.error( "ProducerRunnable failed to produce work, but we will try again after the configured time interval.", e );
}
catch( Throwable t ){
log.error( "ProducerRunnable failed miserably to produce work, the scheduledExecutorService will break now!", t );
throw t;
}
}
}
}