Enable creating an API using Spring Web #5
@ -6,7 +6,7 @@ import se.su.dsv.scipro.file.FileDescription;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
class PrintingMailer implements Mailer {
|
||||
public class PrintingMailer implements Mailer {
|
||||
@Override
|
||||
public MailResult mail(final String fromName, final String fromEmail, final String[] recipients, final String subject, final String message, final FileDescription attachment) {
|
||||
return new MailResult() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package se.su.dsv.scipro.match;
|
||||
|
||||
class AllowAllIdeaCreationJudge implements IdeaCreationJudge {
|
||||
public class AllowAllIdeaCreationJudge implements IdeaCreationJudge {
|
||||
@Override
|
||||
public Decision ruling(Idea idea) {
|
||||
return Decision.allowed();
|
||||
|
@ -22,8 +22,7 @@ public abstract class AbstractWorker implements Worker {
|
||||
private WorkerData wd = null;
|
||||
private Date lastSuccessfulRun = new Date(0);
|
||||
private boolean successfulWorker = true;
|
||||
private Provider<EntityManager> emProvider;
|
||||
private EntityTransaction transaction;
|
||||
private WorkerTransactionManager txManager;
|
||||
|
||||
/**
|
||||
* Subclasses must be annotated with @Component or similar annotation in order for autowiring of dependencies to work
|
||||
@ -37,16 +36,13 @@ public abstract class AbstractWorker implements Worker {
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setTxManager(Provider<EntityManager> em) {
|
||||
this.emProvider = em;
|
||||
public void setTxManager(WorkerTransactionManager txManager) {
|
||||
this.txManager = txManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
EntityManager em = emProvider.get();
|
||||
try {
|
||||
transaction = em.getTransaction();
|
||||
|
||||
wd = workerDataService.getWorkerDataByName(getName());
|
||||
if (wd != null) {
|
||||
lastSuccessfulRun = wd.getLastSuccessfulRun();
|
||||
@ -72,9 +68,7 @@ public abstract class AbstractWorker implements Worker {
|
||||
setSuccessfulWorker(false);
|
||||
}
|
||||
finally {
|
||||
if (transaction.isActive() && em.isOpen()) {
|
||||
transaction.rollback();
|
||||
}
|
||||
txManager.rollbackIfActive();
|
||||
}
|
||||
|
||||
// in case a job crashes or clears the cache (so the entity is detached)
|
||||
@ -87,9 +81,7 @@ public abstract class AbstractWorker implements Worker {
|
||||
saveWorkerData();
|
||||
|
||||
} finally {
|
||||
if (transaction.isActive() && em.isOpen()) {
|
||||
transaction.rollback();
|
||||
}
|
||||
txManager.rollbackIfActive();
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,15 +131,15 @@ public abstract class AbstractWorker implements Worker {
|
||||
protected abstract void doWork();
|
||||
|
||||
protected void beginTransaction() {
|
||||
transaction.begin();
|
||||
txManager.begin();
|
||||
}
|
||||
|
||||
protected void commitTransaction() {
|
||||
transaction.commit();
|
||||
txManager.commit();
|
||||
}
|
||||
|
||||
protected void rollbackTransaction() {
|
||||
transaction.rollback();
|
||||
txManager.rollback();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,9 +15,9 @@ public class WorkerModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
Multibinder.newSetBinder(binder(), Lifecycle.class).addBinding().to(SchedulerImpl.class);
|
||||
//Multibinder.newSetBinder(binder(), Lifecycle.class).addBinding().to(SchedulerImpl.class);
|
||||
bind(ScheduledExecutorService.class).toInstance(Executors.newScheduledThreadPool(NUMBER_OF_WORKER_THREADS));
|
||||
bind(Scheduler.class).to(SchedulerImpl.class).in(Scopes.SINGLETON);
|
||||
//bind(Scheduler.class).to(SchedulerImpl.class).in(Scopes.SINGLETON);
|
||||
bind(TemporaryWorkerScheduler.class).asEagerSingleton();
|
||||
bind(WorkerDataService.class).to(WorkerDataServiceImpl.class);
|
||||
bind(ThesisUploadDeadlineWorker.class);
|
||||
|
@ -0,0 +1,20 @@
|
||||
package se.su.dsv.scipro.workerthreads;
|
||||
|
||||
public interface WorkerTransactionManager {
|
||||
void rollbackIfActive();
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if a transaction is already active
|
||||
*/
|
||||
void begin();
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if a transaction is not active
|
||||
*/
|
||||
void commit();
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if a transaction is not active
|
||||
*/
|
||||
void rollback();
|
||||
}
|
@ -1,10 +1,6 @@
|
||||
package se.su.dsv.scipro.integration.daisy.workers;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.inject.persist.UnitOfWork;
|
||||
import com.google.inject.util.Providers;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityTransaction;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@ -23,6 +19,7 @@ import se.su.dsv.scipro.system.User;
|
||||
import se.su.dsv.scipro.system.UserService;
|
||||
import se.su.dsv.scipro.workerthreads.WorkerData;
|
||||
import se.su.dsv.scipro.workerthreads.WorkerDataService;
|
||||
import se.su.dsv.scipro.workerthreads.WorkerTransactionManager;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ -47,21 +44,16 @@ public class GradingCompletedMilestoneActivatorTest {
|
||||
@Mock
|
||||
private EventBus eventBus;
|
||||
@Mock
|
||||
private UnitOfWork unitOfWork;
|
||||
@Mock
|
||||
private EntityManager entityManager;
|
||||
@Mock
|
||||
private EntityTransaction entityTransaction;
|
||||
private WorkerTransactionManager workerTransactionManager;
|
||||
@Mock
|
||||
private WorkerDataService workerDataService;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
gradingCompletedMilestoneActivator = new GradingCompletedMilestoneActivator(projectService, daisyAPI, eventBus, userService);
|
||||
gradingCompletedMilestoneActivator.setTxManager(Providers.of(entityManager));
|
||||
gradingCompletedMilestoneActivator.setTxManager(workerTransactionManager);
|
||||
gradingCompletedMilestoneActivator.setWorkerDataService(workerDataService);
|
||||
when(workerDataService.save(any(WorkerData.class))).thenReturn(new WorkerData());
|
||||
when(entityManager.getTransaction()).thenReturn(entityTransaction);
|
||||
|
||||
project.setId(3493L);
|
||||
project.setIdentifier(234);
|
||||
|
@ -1,10 +1,6 @@
|
||||
package se.su.dsv.scipro.integration.daisy.workers;
|
||||
|
||||
import com.google.inject.persist.UnitOfWork;
|
||||
import com.google.inject.util.Providers;
|
||||
import com.querydsl.core.types.Predicate;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityTransaction;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@ -26,6 +22,7 @@ import se.su.dsv.scipro.system.Unit;
|
||||
import se.su.dsv.scipro.system.User;
|
||||
import se.su.dsv.scipro.workerthreads.WorkerData;
|
||||
import se.su.dsv.scipro.workerthreads.WorkerDataService;
|
||||
import se.su.dsv.scipro.workerthreads.WorkerTransactionManager;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
@ -45,10 +42,8 @@ public class ProjectExporterTest {
|
||||
private @Mock ExporterFacade exporterFacade;
|
||||
private @Mock DaisyAPI daisyAPI;
|
||||
private @Mock ExternalExporter externalExporter;
|
||||
private @Mock EntityManager entityManager;
|
||||
private @Mock EntityTransaction entityTransaction;
|
||||
private @Mock WorkerTransactionManager workerTransactionManager;
|
||||
private @Mock WorkerDataService workerDataService;
|
||||
private @Mock UnitOfWork unitOfWork;
|
||||
private @Mock FinalSeminarService finalSeminarService;
|
||||
private @Mock FinalThesisService finalThesisService;
|
||||
|
||||
@ -61,10 +56,9 @@ public class ProjectExporterTest {
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
projectExporter = new ProjectExporter(projectRepo, exporterFacade, daisyAPI, externalExporter, finalSeminarService, finalThesisService);
|
||||
projectExporter.setTxManager(Providers.of(entityManager));
|
||||
projectExporter.setTxManager(workerTransactionManager);
|
||||
projectExporter.setWorkerDataService(workerDataService);
|
||||
when(workerDataService.save(any(WorkerData.class))).thenReturn(new WorkerData());
|
||||
when(entityManager.getTransaction()).thenReturn(entityTransaction);
|
||||
|
||||
Unit unit = new Unit();
|
||||
unit.setIdentifier(239478);
|
||||
|
@ -1,7 +1,5 @@
|
||||
package se.su.dsv.scipro.integration.daisy.workers;
|
||||
|
||||
import com.google.inject.persist.UnitOfWork;
|
||||
import com.google.inject.util.Providers;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@ -18,8 +16,8 @@ import se.su.dsv.scipro.project.QProject;
|
||||
import se.su.dsv.scipro.workerthreads.WorkerData;
|
||||
import se.su.dsv.scipro.workerthreads.WorkerDataService;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityTransaction;
|
||||
import se.su.dsv.scipro.workerthreads.WorkerTransactionManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@ -38,9 +36,7 @@ public class ProjectFinalizerTest {
|
||||
|
||||
private @Mock DaisyAPI daisyAPI;
|
||||
private @Mock ProjectService projectService;
|
||||
private @Mock UnitOfWork unitOfWork;
|
||||
private @Mock EntityManager entityManager;
|
||||
private @Mock EntityTransaction entityTransaction;
|
||||
private @Mock WorkerTransactionManager workerTransactionManager;
|
||||
private @Mock WorkerDataService workerDataService;
|
||||
private @Mock ThesisApprovedHistoryService thesisApprovedHistoryService;
|
||||
|
||||
@ -48,10 +44,9 @@ public class ProjectFinalizerTest {
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
projectFinalizer = new ProjectFinalizer(projectService, daisyAPI, thesisApprovedHistoryService);
|
||||
projectFinalizer.setTxManager(Providers.of(entityManager));
|
||||
projectFinalizer.setTxManager(workerTransactionManager);
|
||||
projectFinalizer.setWorkerDataService(workerDataService);
|
||||
when(workerDataService.save(any(WorkerData.class))).thenReturn(new WorkerData());
|
||||
when(entityManager.getTransaction()).thenReturn(entityTransaction);
|
||||
|
||||
project.setId(3493L);
|
||||
project.setIdentifier(234);
|
||||
|
@ -21,8 +21,6 @@ import se.su.dsv.scipro.sukat.LDAP;
|
||||
import se.su.dsv.scipro.sukat.Sukat;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(
|
||||
@ -34,13 +32,6 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||
".*GradingServiceImpl"
|
||||
}))
|
||||
public class CoreConfig {
|
||||
private static final int NUMBER_OF_WORKER_THREADS = 4;
|
||||
|
||||
@Bean
|
||||
public ScheduledExecutorService scheduledExecutorService() {
|
||||
return Executors.newScheduledThreadPool(NUMBER_OF_WORKER_THREADS);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EventBus eventBus() {
|
||||
return new EventBus();
|
||||
|
45
war/src/main/java/se/su/dsv/scipro/war/MailConfig.java
Normal file
45
war/src/main/java/se/su/dsv/scipro/war/MailConfig.java
Normal file
@ -0,0 +1,45 @@
|
||||
package se.su.dsv.scipro.war;
|
||||
|
||||
import jakarta.mail.Session;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import se.su.dsv.scipro.data.facade.MailFacade;
|
||||
import se.su.dsv.scipro.file.FileService;
|
||||
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
|
||||
import se.su.dsv.scipro.mail.Mail;
|
||||
import se.su.dsv.scipro.mail.Mailer;
|
||||
import se.su.dsv.scipro.mail.PrintingMailer;
|
||||
import se.su.dsv.scipro.mail.RedirectingMailer;
|
||||
import se.su.dsv.scipro.profiles.CurrentProfile;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
@Configuration
|
||||
public class MailConfig {
|
||||
@Bean
|
||||
public MailFacade mailFacade() {
|
||||
return new MailFacade();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Mailer mailer(
|
||||
CurrentProfile currentProfile,
|
||||
Session session,
|
||||
FileService fileDescriptionService)
|
||||
{
|
||||
return switch (currentProfile.getCurrentProfile()) {
|
||||
case DEV -> new PrintingMailer();
|
||||
case PROD -> new Mail(session, fileDescriptionService);
|
||||
case TEST -> new RedirectingMailer(session, "scipro-mailtest@dsv.su.se", fileDescriptionService);
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Session session(GeneralSystemSettingsService generalSystemSettings) {
|
||||
String smtpHost = generalSystemSettings.getGeneralSystemSettingsInstance().getSmtpServer();
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("mail.smtp.host", smtpHost);
|
||||
properties.setProperty("mail.smtp.sendpartial", Boolean.toString(true));
|
||||
return Session.getDefaultInstance(properties);
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ import java.util.Set;
|
||||
|
||||
@SpringBootApplication
|
||||
@EntityScan("se.su.dsv.scipro")
|
||||
@Import({CoreConfig.class, ApiConfig.class})
|
||||
@Import({CoreConfig.class, ApiConfig.class, WorkerConfig.class, MailConfig.class})
|
||||
public class Main extends SpringBootServletInitializer implements ServletContainerInitializer {
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
|
||||
|
@ -0,0 +1,50 @@
|
||||
package se.su.dsv.scipro.war;
|
||||
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import se.su.dsv.scipro.workerthreads.WorkerTransactionManager;
|
||||
|
||||
public class SpringManagedWorkerTransactions implements WorkerTransactionManager {
|
||||
private final PlatformTransactionManager platformTransactionManager;
|
||||
|
||||
private TransactionStatus activeTransaction;
|
||||
|
||||
public SpringManagedWorkerTransactions(PlatformTransactionManager platformTransactionManager) {
|
||||
this.platformTransactionManager = platformTransactionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollbackIfActive() {
|
||||
if (this.activeTransaction != null) {
|
||||
platformTransactionManager.rollback(this.activeTransaction);
|
||||
this.activeTransaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void begin() {
|
||||
if (this.activeTransaction != null) {
|
||||
throw new IllegalStateException("A transaction is already active");
|
||||
}
|
||||
this.activeTransaction = platformTransactionManager.getTransaction(TransactionDefinition.withDefaults());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
if (this.activeTransaction == null) {
|
||||
throw new IllegalStateException("A transaction is not active");
|
||||
}
|
||||
platformTransactionManager.commit(this.activeTransaction);
|
||||
this.activeTransaction = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
if (this.activeTransaction == null) {
|
||||
throw new IllegalStateException("A transaction is not active");
|
||||
}
|
||||
platformTransactionManager.rollback(this.activeTransaction);
|
||||
this.activeTransaction = null;
|
||||
}
|
||||
}
|
94
war/src/main/java/se/su/dsv/scipro/war/WorkerConfig.java
Normal file
94
war/src/main/java/se/su/dsv/scipro/war/WorkerConfig.java
Normal file
@ -0,0 +1,94 @@
|
||||
package se.su.dsv.scipro.war;
|
||||
|
||||
import jakarta.inject.Provider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import se.su.dsv.scipro.file.FileService;
|
||||
import se.su.dsv.scipro.mail.MailEventWorker;
|
||||
import se.su.dsv.scipro.match.AllowAllIdeaCreationJudge;
|
||||
import se.su.dsv.scipro.match.IdeaCreationJudge;
|
||||
import se.su.dsv.scipro.plagiarism.PlagiarismRequestRepository;
|
||||
import se.su.dsv.scipro.plagiarism.PlagiarismSubmitter;
|
||||
import se.su.dsv.scipro.plagiarism.urkund.StatusPollingWorker;
|
||||
import se.su.dsv.scipro.plagiarism.urkund.UrkundService;
|
||||
import se.su.dsv.scipro.plagiarism.urkund.UrkundSettings;
|
||||
import se.su.dsv.scipro.plagiarism.urkund.UrkundSettingsRepository;
|
||||
import se.su.dsv.scipro.projectpartner.RemoveFulfilledPartnerAdsWorker;
|
||||
import se.su.dsv.scipro.reviewing.ReviewerDecisionReminderWorker;
|
||||
import se.su.dsv.scipro.workerthreads.GradeFinalSeminarParticipantReminderWorker;
|
||||
import se.su.dsv.scipro.workerthreads.IdeaExportWorker;
|
||||
import se.su.dsv.scipro.workerthreads.ManualMatchRemindWorker;
|
||||
import se.su.dsv.scipro.workerthreads.NotificationCompilationWorker;
|
||||
import se.su.dsv.scipro.workerthreads.Scheduler;
|
||||
import se.su.dsv.scipro.workerthreads.TemporaryWorkerScheduler;
|
||||
import se.su.dsv.scipro.workerthreads.ThesisUploadDeadlineWorker;
|
||||
import se.su.dsv.scipro.workerthreads.ThesisUploadReminderWorker;
|
||||
import se.su.dsv.scipro.workerthreads.WorkerTransactionManager;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(
|
||||
basePackages = "se.su.dsv.scipro",
|
||||
includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Worker$"),
|
||||
excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*[Dd]aisy.*")
|
||||
)
|
||||
public class WorkerConfig {
|
||||
private static final int NUMBER_OF_WORKER_THREADS = 4;
|
||||
|
||||
@Bean
|
||||
public ScheduledExecutorService scheduledExecutorService() {
|
||||
return Executors.newScheduledThreadPool(NUMBER_OF_WORKER_THREADS);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TemporaryWorkerScheduler temporaryWorkerScheduler(
|
||||
Scheduler scheduler,
|
||||
Provider<MailEventWorker> mailEventWorker,
|
||||
Provider<NotificationCompilationWorker> notificationCompilationWorker,
|
||||
Provider<IdeaExportWorker> ideaExportWorker,
|
||||
Provider<ThesisUploadReminderWorker> thesisUploadReminderWorker,
|
||||
Provider<ThesisUploadDeadlineWorker> thesisUploadDeadlineWorker,
|
||||
Provider<ManualMatchRemindWorker> manualMatchRemindWorkerProvider,
|
||||
Provider<ReviewerDecisionReminderWorker> reviewerDecisionReminderWorker,
|
||||
Provider<PlagiarismSubmitter> plagiarismSubmitter,
|
||||
Provider<StatusPollingWorker> urkundPoller,
|
||||
Provider<RemoveFulfilledPartnerAdsWorker> removeFulfilledPartnerAds,
|
||||
Provider<GradeFinalSeminarParticipantReminderWorker> gradeFinalSeminarParticipantReminderWorkerProvider)
|
||||
{
|
||||
return new TemporaryWorkerScheduler(scheduler, mailEventWorker, notificationCompilationWorker, ideaExportWorker, thesisUploadReminderWorker, thesisUploadDeadlineWorker, manualMatchRemindWorkerProvider, reviewerDecisionReminderWorker, plagiarismSubmitter, urkundPoller, removeFulfilledPartnerAds, gradeFinalSeminarParticipantReminderWorkerProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(IdeaCreationJudge.class)
|
||||
public IdeaCreationJudge ideaCreationJudge() {
|
||||
return new AllowAllIdeaCreationJudge();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PlagiarismSubmitter plagiarismSubmitter(
|
||||
Provider<UrkundSettings> urkundSettings,
|
||||
PlagiarismRequestRepository plagiarismRequestRepository,
|
||||
UrkundService urkundService,
|
||||
FileService fileService)
|
||||
{
|
||||
return new PlagiarismSubmitter(urkundSettings, plagiarismRequestRepository, urkundService, fileService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UrkundSettings urkundSettings(UrkundSettingsRepository urkundSettingsRepository) {
|
||||
return urkundSettingsRepository.getSettings();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public WorkerTransactionManager workerTransactionManager(PlatformTransactionManager platformTransactionManager) {
|
||||
return new SpringManagedWorkerTransactions(platformTransactionManager);
|
||||
}
|
||||
}
|
@ -1,12 +1,24 @@
|
||||
package se.su.dsv.scipro.workerthreads;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
import jakarta.servlet.FilterChain;
|
||||
ansv7779 marked this conversation as resolved
Outdated
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
|
||||
import org.springframework.orm.jpa.EntityManagerHolder;
|
||||
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import se.su.dsv.scipro.system.Lifecycle;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Provider;
|
||||
import jakarta.inject.Singleton;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@ -19,13 +31,15 @@ public class SchedulerImpl implements Lifecycle, Scheduler {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SchedulerImpl.class);
|
||||
|
||||
private final ScheduledExecutorService scheduledExecutorService;
|
||||
private final EntityManagerFactory emf;
|
||||
|
||||
private final Set<Task> tasks = new TreeSet<>(new Task.ByDescriptionComparator());
|
||||
private final Set<Task> runningWorkers = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
@Inject
|
||||
public SchedulerImpl(ScheduledExecutorService scheduledExecutorService) {
|
||||
public SchedulerImpl(ScheduledExecutorService scheduledExecutorService, EntityManagerFactory emf) {
|
||||
this.scheduledExecutorService = scheduledExecutorService;
|
||||
this.emf = emf;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -43,6 +57,14 @@ public class SchedulerImpl implements Lifecycle, Scheduler {
|
||||
private Runnable tracked(final Task task) {
|
||||
return () -> {
|
||||
try {
|
||||
// Since we're not in a web request when running a scheduled job, Spring's OpenEntityManagerInView
|
||||
// filter is not active. Therefore, we need to manually bind a new EntityManager to the current thread
|
||||
// to be shared by all DAOs. If we do not, we will get problems with detached entity passed to persist
|
||||
// since every read and write from the database will be in a new EntityManager.
|
||||
EntityManager em = emf.createEntityManager();
|
||||
EntityManagerHolder emHolder = new EntityManagerHolder(em);
|
||||
TransactionSynchronizationManager.bindResource(emf, emHolder);
|
||||
|
||||
runningWorkers.add(task);
|
||||
task.execute();
|
||||
}
|
||||
@ -51,6 +73,11 @@ public class SchedulerImpl implements Lifecycle, Scheduler {
|
||||
}
|
||||
finally {
|
||||
runningWorkers.remove(task);
|
||||
|
||||
// Clean up the shared EntityManager
|
||||
EntityManagerHolder emHolder = (EntityManagerHolder)
|
||||
TransactionSynchronizationManager.unbindResource(emf);
|
||||
EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
|
||||
}
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user
Clean up unused imports