package ee.telekom.workflow.executor.consumer;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import ee.telekom.workflow.core.common.WorkflowEngineConfiguration;
import ee.telekom.workflow.core.notification.ExceptionNotificationService;
import ee.telekom.workflow.util.ExecutorServiceUtil;
import ee.telekom.workflow.util.NamedPoolThreadFactory;
@Component
public class WorkConsumerJobImpl implements WorkConsumerJob{
private static final Logger log = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() );
@Autowired
private WorkConsumerService workConsumerService;
@Autowired
private WorkflowEngineConfiguration config;
@Autowired
private ExceptionNotificationService exceptionNotificationService;
private ExecutorService executorService;
private final AtomicBoolean isStopping = new AtomicBoolean();
@Override
public synchronized void start(){
isStopping.set( false );
// number of parallel consumer threads
int numberOfConsumerThreads = config.getNumberOfConsumerThreads();
// spring security context for executor threads
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("workflow-engine", "[not-used]", AuthorityUtils.createAuthorityList("ROLE_WORKFLOW_ENGINE")));
// actual executor thread pool
ExecutorService delegateExecutorService = Executors.newFixedThreadPool( numberOfConsumerThreads, new NamedPoolThreadFactory( "consumer" ) );
// wrapper executor service that sets the security context for each thread
executorService = new DelegatingSecurityContextExecutorService(delegateExecutorService, securityContext);
// start the consuming jobs
for( int i = 0; i < numberOfConsumerThreads; i++ ){
executorService.execute( new ConsumerRunnable() );
}
log.info( "Scheduled {} consumers", numberOfConsumerThreads );
}
@Override
public synchronized void stop(){
log.debug( "Stopping consumers" );
isStopping.set( true );
ExecutorServiceUtil.shutDownSynchronously( executorService );
log.info( "Stopped all consumers" );
}
private class ConsumerRunnable implements Runnable{
@Override
public void run(){
try{
log.info( "Started consumer on thread {}", Thread.currentThread().getName() );
while( !isStopping.get() ){
try{
workConsumerService.consumeWorkUnit();
}
catch( Exception e ){
log.error( "ConsumerRunnable failed to consume work, but we will try again after 10 seconds.", e );
exceptionNotificationService.handleException( e );
try{
Thread.sleep( 1000L * 10 );
}
catch( InterruptedException ie ){
// do nothing
}
}
}
log.info( "Stopped consumer on thread {}", Thread.currentThread().getName() );
}
catch( Throwable t ){
log.error( "ConsumerRunnable failed miserably to consume work, the fixed executor thread will die now!", t );
throw t;
}
}
}
}