package org.molgenis.data.jobs; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.transaction.TransactionException; import org.springframework.transaction.support.TransactionTemplate; import java.util.Objects; import java.util.concurrent.Callable; /** * Superclass for molgenis jobs that keeps track of their progress. */ public abstract class Job<Result> implements Callable<Result> { private static final Logger LOG = LoggerFactory.getLogger(Job.class); private final Progress progress; private TransactionTemplate transactionTemplate; private Authentication authentication; public Job(Progress progress, TransactionTemplate transactionTemplate, Authentication authentication) { this.progress = Objects.requireNonNull(progress); this.transactionTemplate = transactionTemplate; this.authentication = Objects.requireNonNull(authentication); } @Override public Result call() { progress.start(); try { Result result; if (transactionTemplate != null) { result = transactionTemplate.execute((status) -> tryRunWithAuthentication()); } else { result = tryRunWithAuthentication(); } progress.success(); return result; } catch (JobExecutionException ex) { Exception cause = (Exception) ex.getCause(); LOG.warn("Error executing job", cause); progress.failed(cause); throw ex; } catch (TransactionException te) { LOG.error("Error rolling back transaction for failed job execution", te); progress.failed(te); throw te; } catch (Exception other) { LOG.error("Error logging job success", other); progress.failed(other); throw other; } } private Result tryRunWithAuthentication() { try { return runWithAuthentication(); } catch (Exception e) { throw new JobExecutionException(e); } } private Result runWithAuthentication() throws Exception { SecurityContext originalContext = SecurityContextHolder.getContext(); try { SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext()); SecurityContextHolder.getContext().setAuthentication(authentication); return call(progress); } finally { SecurityContextHolder.setContext(originalContext); } } /** * Executes this job. For concrete subclasses to implement. * * @param progress * @throws Exception */ public abstract Result call(Progress progress) throws Exception; }