diff --git a/src/main/java/edu/ohsu/cmp/coach/COACHApplication.java b/src/main/java/edu/ohsu/cmp/coach/COACHApplication.java index e9c298ad..a281299a 100644 --- a/src/main/java/edu/ohsu/cmp/coach/COACHApplication.java +++ b/src/main/java/edu/ohsu/cmp/coach/COACHApplication.java @@ -1,12 +1,13 @@ package edu.ohsu.cmp.coach; +import edu.ohsu.cmp.coach.config.RedcapConfigurationValidator; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; - -import edu.ohsu.cmp.coach.config.RedcapConfigurationValidator; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling public class COACHApplication { public static void main(String[] args) { SpringApplication.run(COACHApplication.class, args); diff --git a/src/main/java/edu/ohsu/cmp/coach/model/omron/RefreshTokenJob.java b/src/main/java/edu/ohsu/cmp/coach/model/omron/RefreshTokenJob.java index 2892af54..6d561fd9 100644 --- a/src/main/java/edu/ohsu/cmp/coach/model/omron/RefreshTokenJob.java +++ b/src/main/java/edu/ohsu/cmp/coach/model/omron/RefreshTokenJob.java @@ -32,19 +32,24 @@ public void execute(JobExecutionContext jobExecutionContext) throws JobExecution String refreshToken = jobDataMap.getString(JOBDATA_REFRESHTOKEN); UserWorkspaceService userWorkspaceService = ctx.getBean(UserWorkspaceService.class); - OmronService omronService = ctx.getBean(OmronService.class); - - try { - RefreshTokenResponse response = omronService.refreshAccessToken(refreshToken); - if (response != null) { - UserWorkspace workspace = userWorkspaceService.get(sessionId); - workspace.getOmronTokenData().update(response); - omronService.scheduleAccessTokenRefresh(sessionId); + if (userWorkspaceService.exists(sessionId)) { + OmronService omronService = ctx.getBean(OmronService.class); + + try { + RefreshTokenResponse response = omronService.refreshAccessToken(refreshToken); + if (response != null) { + UserWorkspace workspace = userWorkspaceService.get(sessionId); + workspace.getOmronTokenData().update(response); + omronService.scheduleAccessTokenRefresh(sessionId); + } + + } catch (Exception e) { + throw new JobExecutionException("caught " + e.getClass().getName() + " executing job for session=" + + sessionId + " - " + e.getMessage(), e); } - } catch (Exception e) { - throw new JobExecutionException("caught " + e.getClass().getName() + " executing job for session=" + - sessionId + " - " + e.getMessage(), e); + } else { + logger.info("aborting job {} - user workspace for session={} does not exist", name, sessionId); } } } diff --git a/src/main/java/edu/ohsu/cmp/coach/workspace/UserWorkspace.java b/src/main/java/edu/ohsu/cmp/coach/workspace/UserWorkspace.java index b5503e14..5bd999bd 100644 --- a/src/main/java/edu/ohsu/cmp/coach/workspace/UserWorkspace.java +++ b/src/main/java/edu/ohsu/cmp/coach/workspace/UserWorkspace.java @@ -224,6 +224,7 @@ public void clearVitalsCaches() { } public void shutdown() { + logger.info("shutting down workspace for session=" + sessionId); executorService.shutdown(); clearCaches(); diff --git a/src/main/java/edu/ohsu/cmp/coach/workspace/UserWorkspaceService.java b/src/main/java/edu/ohsu/cmp/coach/workspace/UserWorkspaceService.java index 714a1e17..e1beeb61 100644 --- a/src/main/java/edu/ohsu/cmp/coach/workspace/UserWorkspaceService.java +++ b/src/main/java/edu/ohsu/cmp/coach/workspace/UserWorkspaceService.java @@ -1,23 +1,30 @@ package edu.ohsu.cmp.coach.workspace; +import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import edu.ohsu.cmp.coach.entity.RandomizationGroup; import edu.ohsu.cmp.coach.exception.ConfigurationException; import edu.ohsu.cmp.coach.exception.SessionMissingException; import edu.ohsu.cmp.coach.fhir.FhirConfigManager; import edu.ohsu.cmp.coach.fhir.FhirQueryManager; import edu.ohsu.cmp.coach.fhir.transform.VendorTransformer; +import edu.ohsu.cmp.coach.model.Audience; +import edu.ohsu.cmp.coach.model.AuditLevel; import edu.ohsu.cmp.coach.model.MyOmronTokenData; import edu.ohsu.cmp.coach.model.fhir.FHIRCredentialsWithClient; -import edu.ohsu.cmp.coach.model.Audience; +import edu.ohsu.cmp.coach.service.AuditService; +import edu.ohsu.cmp.coach.service.EHRService; import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.r4.model.Patient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.lang.reflect.InvocationTargetException; +import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -43,6 +50,41 @@ public UserWorkspaceService() { map = new ConcurrentHashMap<>(); } + @Scheduled(cron = "0 0 * * * *") // top of every hour, every day + public void shutdownExpiredWorkspaces() { + if (map.isEmpty()) return; + + if (map.size() == 1) logger.info("checking for expired workspaces (1 workspace registered) -"); + else logger.info("checking for expired workspaces (" + map.size() + " workspaces registered) -"); + + EHRService ehrService = ctx.getBean(EHRService.class); + AuditService auditService = ctx.getBean(AuditService.class); + + Iterator> iter = map.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + String sessionId = entry.getKey(); + try { + // if we can get the Patient resource, the access token is still valid, and the workspace should persist + Patient p = ehrService.getPatient(sessionId); + logger.debug("successfully retrieved Patient resource with id=" + p.getId() + " for session=" + sessionId + " - workspace is valid"); + + } catch (AuthenticationException ae) { + logger.info("credentials expired for session " + sessionId + " - shutting down associated workspace -"); + auditService.doAudit(sessionId, AuditLevel.INFO, "session expired", sessionId); + UserWorkspace workspace = entry.getValue(); + workspace.shutdown(); + iter.remove(); + + } catch (Exception e) { + logger.error("caught " + e.getClass().getName() + " retrieving Patient resource for session=" + sessionId + " - " + e.getMessage(), e); + } + } + + if (map.size() == 1) logger.info("done. (1 workspace remains)"); + else logger.info("done. (" + map.size() + " workspaces remain)"); + } + public boolean exists(String sessionId) { return map.containsKey(sessionId); }