
001package ca.uhn.fhir.jpa.util; 002 003/*- 004 * #%L 005 * HAPI FHIR JPA Server 006 * %% 007 * Copyright (C) 2014 - 2022 Smile CDR, Inc. 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.jpa.model.sched.HapiJob; 025import ca.uhn.fhir.jpa.model.sched.ISchedulerService; 026import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; 027import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 028import com.google.common.annotations.VisibleForTesting; 029import org.apache.commons.lang3.time.DateUtils; 030import org.quartz.JobExecutionContext; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033import org.springframework.beans.factory.annotation.Autowired; 034 035import javax.annotation.PostConstruct; 036import java.util.Map; 037import java.util.concurrent.Callable; 038import java.util.concurrent.atomic.AtomicReference; 039 040public class ResourceCountCache { 041 042 private static final Logger ourLog = LoggerFactory.getLogger(ResourceCountCache.class); 043 private static Long ourNowForUnitTest; 044 private final Callable<Map<String, Long>> myFetcher; 045 private volatile long myCacheMillis; 046 private AtomicReference<Map<String, Long>> myCapabilityStatement = new AtomicReference<>(); 047 private long myLastFetched; 048 @Autowired 049 private ISchedulerService mySchedulerService; 050 051 /** 052 * Constructor 053 */ 054 public ResourceCountCache(Callable<Map<String, Long>> theFetcher) { 055 myFetcher = theFetcher; 056 } 057 058 public synchronized void clear() { 059 ourLog.info("Clearing cache"); 060 myCapabilityStatement.set(null); 061 myLastFetched = 0; 062 } 063 064 public synchronized Map<String, Long> get() { 065 return myCapabilityStatement.get(); 066 } 067 068 private Map<String, Long> refresh() { 069 Map<String, Long> retVal; 070 try { 071 retVal = myFetcher.call(); 072 } catch (Exception e) { 073 throw new InternalErrorException(Msg.code(799) + e); 074 } 075 076 myCapabilityStatement.set(retVal); 077 myLastFetched = now(); 078 return retVal; 079 } 080 081 public void setCacheMillis(long theCacheMillis) { 082 myCacheMillis = theCacheMillis; 083 } 084 085 public void update() { 086 if (myCacheMillis > 0) { 087 long now = now(); 088 long expiry = now - myCacheMillis; 089 if (myLastFetched < expiry) { 090 refresh(); 091 } 092 } 093 } 094 095 @PostConstruct 096 public void scheduleJob() { 097 ScheduledJobDefinition jobDetail = new ScheduledJobDefinition(); 098 jobDetail.setId(getClass().getName()); 099 jobDetail.setJobClass(Job.class); 100 mySchedulerService.scheduleLocalJob(10 * DateUtils.MILLIS_PER_MINUTE, jobDetail); 101 } 102 103 public static class Job implements HapiJob { 104 @Autowired 105 private ResourceCountCache myTarget; 106 107 @Override 108 public void execute(JobExecutionContext theContext) { 109 myTarget.update(); 110 } 111 } 112 113 private static long now() { 114 if (ourNowForUnitTest != null) { 115 return ourNowForUnitTest; 116 } 117 return System.currentTimeMillis(); 118 } 119 120 @VisibleForTesting 121 static void setNowForUnitTest(Long theNowForUnitTest) { 122 ourNowForUnitTest = theNowForUnitTest; 123 } 124 125 126}