001package ca.uhn.fhir.jpa.api.dao; 002 003/*- 004 * #%L 005 * HAPI FHIR Storage api 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.context.FhirContext; 025import ca.uhn.fhir.context.RuntimeResourceDefinition; 026import ca.uhn.fhir.jpa.api.IDaoRegistry; 027import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum; 028import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 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 javax.annotation.Nullable; 037import java.util.Arrays; 038import java.util.Collection; 039import java.util.Collections; 040import java.util.HashMap; 041import java.util.HashSet; 042import java.util.List; 043import java.util.Map; 044import java.util.Set; 045import java.util.stream.Collectors; 046 047public class DaoRegistry implements ApplicationContextAware, IDaoRegistry { 048 private ApplicationContext myAppCtx; 049 050 @Autowired 051 private FhirContext myContext; 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 081 @Override 082 public void setApplicationContext(ApplicationContext theApplicationContext) throws BeansException { 083 myAppCtx = theApplicationContext; 084 } 085 086 public IFhirSystemDao getSystemDao() { 087 IFhirSystemDao retVal = mySystemDao; 088 if (retVal == null) { 089 retVal = myAppCtx.getBean(IFhirSystemDao.class); 090 mySystemDao = retVal; 091 } 092 return retVal; 093 } 094 095 /** 096 * @throws InvalidRequestException If the given resource type is not supported 097 */ 098 public IFhirResourceDao getResourceDao(String theResourceName) { 099 IFhirResourceDao<IBaseResource> retVal = getResourceDaoOrNull(theResourceName); 100 if (retVal == null) { 101 List<String> supportedResourceTypes = myResourceNameToResourceDao 102 .keySet() 103 .stream() 104 .sorted() 105 .collect(Collectors.toList()); 106 throw new InvalidRequestException(Msg.code(572) + "Unable to process request, this server does not know how to handle resources of type " + theResourceName + " - Can handle: " + supportedResourceTypes); 107 } 108 return retVal; 109 } 110 111 public <R extends IBaseResource> IFhirResourceDao<R> getResourceDao(Class<R> theResourceType) { 112 IFhirResourceDao<R> retVal = getResourceDaoIfExists(theResourceType); 113 Validate.notNull(retVal, "No DAO exists for resource type %s - Have: %s", theResourceType, myResourceNameToResourceDao); 114 return retVal; 115 } 116 117 /** 118 * Use getResourceDaoOrNull 119 */ 120 @Deprecated 121 public <T extends IBaseResource> IFhirResourceDao<T> getResourceDaoIfExists(Class<T> theResourceType) { 122 return getResourceDaoOrNull(theResourceType); 123 } 124 125 @Nullable 126 public <T extends IBaseResource> IFhirResourceDao<T> getResourceDaoOrNull(Class<T> theResourceType) { 127 String resourceName = myContext.getResourceType(theResourceType); 128 try { 129 return (IFhirResourceDao<T>) getResourceDao(resourceName); 130 } catch (InvalidRequestException e) { 131 return null; 132 } 133 } 134 135 /** 136 * Use getResourceDaoOrNull 137 */ 138 @Deprecated 139 public <T extends IBaseResource> IFhirResourceDao<T> getResourceDaoIfExists(String theResourceType) { 140 return getResourceDaoOrNull(theResourceType); 141 } 142 143 @Nullable 144 public <T extends IBaseResource> IFhirResourceDao<T> getResourceDaoOrNull(String theResourceName) { 145 init(); 146 return (IFhirResourceDao<T>) myResourceNameToResourceDao.get(theResourceName); 147 } 148 149 @Override 150 public boolean isResourceTypeSupported(String theResourceType) { 151 if (mySupportedResourceTypes == null) { 152 return getResourceDaoOrNull(theResourceType) != null; 153 } 154 return mySupportedResourceTypes.contains(theResourceType); 155 } 156 157 private void init() { 158 if (myResourceNameToResourceDao != null && !myResourceNameToResourceDao.isEmpty()) { 159 return; 160 } 161 162 Map<String, IFhirResourceDao> resourceDaos = myAppCtx.getBeansOfType(IFhirResourceDao.class); 163 164 initializeMaps(resourceDaos.values()); 165 } 166 167 private void initializeMaps(Collection<IFhirResourceDao> theResourceDaos) { 168 169 myResourceNameToResourceDao = new HashMap<>(); 170 171 for (IFhirResourceDao nextResourceDao : theResourceDaos) { 172 Class resourceType = nextResourceDao.getResourceType(); 173 assert resourceType != null; 174 RuntimeResourceDefinition nextResourceDef = myContext.getResourceDefinition(resourceType); 175 if (mySupportedResourceTypes == null || mySupportedResourceTypes.contains(nextResourceDef.getName())) { 176 myResourceNameToResourceDao.put(nextResourceDef.getName(), nextResourceDao); 177 } 178 } 179 } 180 181 public void register(IFhirResourceDao theResourceDao) { 182 RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(theResourceDao.getResourceType()); 183 String resourceName = resourceDef.getName(); 184 myResourceNameToResourceDao.put(resourceName, theResourceDao); 185 } 186 187 public IFhirResourceDao getDaoOrThrowException(Class<? extends IBaseResource> theClass) { 188 IFhirResourceDao retVal = getResourceDao(theClass); 189 if (retVal == null) { 190 List<String> supportedResourceNames = myResourceNameToResourceDao 191 .keySet() 192 .stream() 193 .map(t -> myContext.getResourceType(t)) 194 .sorted() 195 .collect(Collectors.toList()); 196 throw new InvalidRequestException(Msg.code(573) + "Unable to process request, this server does not know how to handle resources of type " + myContext.getResourceType(theClass) + " - Can handle: " + supportedResourceNames); 197 } 198 return retVal; 199 } 200 201 public void setResourceDaos(Collection<IFhirResourceDao> theResourceDaos) { 202 initializeMaps(theResourceDaos); 203 } 204 205 public IFhirResourceDao getSubscriptionDao() { 206 return getResourceDao(ResourceTypeEnum.SUBSCRIPTION.getCode()); 207 } 208 209 public void setSupportedResourceTypes(String... theResourceTypes) { 210 setSupportedResourceTypes(toCollection(theResourceTypes)); 211 } 212 213 private List<String> toCollection(String[] theResourceTypes) { 214 List<String> retVal = null; 215 if (theResourceTypes != null && theResourceTypes.length > 0) { 216 retVal = Arrays.asList(theResourceTypes); 217 } 218 return retVal; 219 } 220 221 public Set<String> getRegisteredDaoTypes() { 222 return Collections.unmodifiableSet(myResourceNameToResourceDao.keySet()); 223 } 224}