001/*- 002 * #%L 003 * HAPI FHIR Storage api 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.api.dao; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.context.RuntimeResourceDefinition; 024import ca.uhn.fhir.i18n.Msg; 025import ca.uhn.fhir.jpa.api.IDaoRegistry; 026import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum; 027import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 028import jakarta.annotation.Nullable; 029import org.apache.commons.lang3.Validate; 030import org.hl7.fhir.instance.model.api.IBaseResource; 031import org.springframework.beans.BeansException; 032import org.springframework.beans.factory.annotation.Autowired; 033import org.springframework.context.ApplicationContext; 034import org.springframework.context.ApplicationContextAware; 035 036import java.util.Arrays; 037import java.util.Collection; 038import java.util.Collections; 039import java.util.HashMap; 040import java.util.HashSet; 041import java.util.List; 042import java.util.Map; 043import java.util.Set; 044import java.util.stream.Collectors; 045 046public class DaoRegistry implements ApplicationContextAware, IDaoRegistry { 047 private ApplicationContext myAppCtx; 048 049 @Autowired 050 private FhirContext myContext; 051 052 private volatile Map<String, IFhirResourceDao<?>> myResourceNameToResourceDao; 053 private volatile IFhirSystemDao<?, ?> mySystemDao; 054 private Set<String> mySupportedResourceTypes; 055 056 /** 057 * Constructor 058 */ 059 public DaoRegistry() { 060 this(null); 061 } 062 063 /** 064 * Constructor 065 */ 066 public DaoRegistry(FhirContext theFhirContext) { 067 super(); 068 myContext = theFhirContext; 069 } 070 071 public void setSupportedResourceTypes(Collection<String> theSupportedResourceTypes) { 072 HashSet<String> supportedResourceTypes = new HashSet<>(); 073 if (theSupportedResourceTypes != null) { 074 supportedResourceTypes.addAll(theSupportedResourceTypes); 075 } 076 mySupportedResourceTypes = supportedResourceTypes; 077 myResourceNameToResourceDao = null; 078 } 079 080 @Override 081 public void setApplicationContext(ApplicationContext theApplicationContext) throws BeansException { 082 myAppCtx = theApplicationContext; 083 } 084 085 public IFhirSystemDao getSystemDao() { 086 IFhirSystemDao retVal = mySystemDao; 087 if (retVal == null) { 088 retVal = myAppCtx.getBean(IFhirSystemDao.class); 089 mySystemDao = retVal; 090 } 091 return retVal; 092 } 093 094 /** 095 * @throws InvalidRequestException If the given resource type is not supported 096 */ 097 public IFhirResourceDao getResourceDao(String theResourceName) { 098 IFhirResourceDao<IBaseResource> retVal = getResourceDaoOrNull(theResourceName); 099 if (retVal == null) { 100 List<String> supportedResourceTypes = 101 myResourceNameToResourceDao.keySet().stream().sorted().collect(Collectors.toList()); 102 throw new InvalidRequestException(Msg.code(572) 103 + "Unable to process request, this server does not know how to handle resources of type " 104 + theResourceName + " - Can handle: " + supportedResourceTypes); 105 } 106 return retVal; 107 } 108 109 @SuppressWarnings("unchecked") 110 public <R extends IBaseResource> IFhirResourceDao<R> getResourceDao(R theResource) { 111 return (IFhirResourceDao<R>) getResourceDao(theResource.getClass()); 112 } 113 114 public <R extends IBaseResource> IFhirResourceDao<R> getResourceDao(Class<R> theResourceType) { 115 IFhirResourceDao<R> retVal = getResourceDaoIfExists(theResourceType); 116 Validate.notNull( 117 retVal, "No DAO exists for resource type %s - Have: %s", theResourceType, myResourceNameToResourceDao); 118 return retVal; 119 } 120 121 /** 122 * Use getResourceDaoOrNull 123 */ 124 @Deprecated 125 public <T extends IBaseResource> IFhirResourceDao<T> getResourceDaoIfExists(Class<T> theResourceType) { 126 return getResourceDaoOrNull(theResourceType); 127 } 128 129 @Nullable 130 public <T extends IBaseResource> IFhirResourceDao<T> getResourceDaoOrNull(Class<T> theResourceType) { 131 String resourceName = myContext.getResourceType(theResourceType); 132 try { 133 return (IFhirResourceDao<T>) getResourceDao(resourceName); 134 } catch (InvalidRequestException e) { 135 return null; 136 } 137 } 138 139 /** 140 * Use getResourceDaoOrNull 141 */ 142 @Deprecated 143 public <T extends IBaseResource> IFhirResourceDao<T> getResourceDaoIfExists(String theResourceType) { 144 return getResourceDaoOrNull(theResourceType); 145 } 146 147 @Nullable 148 public <T extends IBaseResource> IFhirResourceDao<T> getResourceDaoOrNull(String theResourceName) { 149 init(); 150 return (IFhirResourceDao<T>) myResourceNameToResourceDao.get(theResourceName); 151 } 152 153 @Override 154 public boolean isResourceTypeSupported(String theResourceType) { 155 if (mySupportedResourceTypes == null) { 156 return getResourceDaoOrNull(theResourceType) != null; 157 } 158 return mySupportedResourceTypes.contains(theResourceType); 159 } 160 161 private void init() { 162 if (myResourceNameToResourceDao != null && !myResourceNameToResourceDao.isEmpty()) { 163 return; 164 } 165 166 Map<String, IFhirResourceDao> resourceDaos = myAppCtx.getBeansOfType(IFhirResourceDao.class); 167 168 initializeMaps(resourceDaos.values()); 169 } 170 171 private void initializeMaps(Collection<IFhirResourceDao> theResourceDaos) { 172 173 myResourceNameToResourceDao = new HashMap<>(); 174 175 for (IFhirResourceDao nextResourceDao : theResourceDaos) { 176 Class resourceType = nextResourceDao.getResourceType(); 177 assert resourceType != null; 178 RuntimeResourceDefinition nextResourceDef = myContext.getResourceDefinition(resourceType); 179 if (mySupportedResourceTypes == null || mySupportedResourceTypes.contains(nextResourceDef.getName())) { 180 myResourceNameToResourceDao.put(nextResourceDef.getName(), nextResourceDao); 181 } 182 } 183 } 184 185 public void register(IFhirResourceDao theResourceDao) { 186 RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(theResourceDao.getResourceType()); 187 String resourceName = resourceDef.getName(); 188 myResourceNameToResourceDao.put(resourceName, theResourceDao); 189 } 190 191 public IFhirResourceDao getDaoOrThrowException(Class<? extends IBaseResource> theClass) { 192 IFhirResourceDao retVal = getResourceDao(theClass); 193 if (retVal == null) { 194 List<String> supportedResourceNames = myResourceNameToResourceDao.keySet().stream() 195 .map(t -> myContext.getResourceType(t)) 196 .sorted() 197 .collect(Collectors.toList()); 198 throw new InvalidRequestException(Msg.code(573) 199 + "Unable to process request, this server does not know how to handle resources of type " 200 + myContext.getResourceType(theClass) + " - Can handle: " + supportedResourceNames); 201 } 202 return retVal; 203 } 204 205 public void setResourceDaos(Collection<IFhirResourceDao> theResourceDaos) { 206 initializeMaps(theResourceDaos); 207 } 208 209 public IFhirResourceDao getSubscriptionDao() { 210 return getResourceDao(ResourceTypeEnum.SUBSCRIPTION.getCode()); 211 } 212 213 public void setSupportedResourceTypes(String... theResourceTypes) { 214 setSupportedResourceTypes(toCollection(theResourceTypes)); 215 } 216 217 private List<String> toCollection(String[] theResourceTypes) { 218 List<String> retVal = null; 219 if (theResourceTypes != null && theResourceTypes.length > 0) { 220 retVal = Arrays.asList(theResourceTypes); 221 } 222 return retVal; 223 } 224 225 public Set<String> getRegisteredDaoTypes() { 226 return Collections.unmodifiableSet(myResourceNameToResourceDao.keySet()); 227 } 228}