001package ca.uhn.fhir.jpa.api.dao;
002
003/*-
004 * #%L
005 * HAPI FHIR JPA API
006 * %%
007 * Copyright (C) 2014 - 2021 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.context.FhirContext;
024import ca.uhn.fhir.context.RuntimeResourceDefinition;
025import ca.uhn.fhir.jpa.api.IDaoRegistry;
026import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
027import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
028import org.apache.commons.lang3.Validate;
029import org.hl7.fhir.instance.model.api.IBaseResource;
030import org.springframework.beans.BeansException;
031import org.springframework.beans.factory.annotation.Autowired;
032import org.springframework.context.ApplicationContext;
033import org.springframework.context.ApplicationContextAware;
034
035import javax.annotation.Nullable;
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        private volatile Map<String, IFhirResourceDao<?>> myResourceNameToResourceDao;
052        private volatile IFhirSystemDao<?, ?> mySystemDao;
053        private Set<String> mySupportedResourceTypes;
054
055        /**
056         * Constructor
057         */
058        public DaoRegistry() {
059                this(null);
060        }
061
062        /**
063         * Constructor
064         */
065        public DaoRegistry(FhirContext theFhirContext) {
066                super();
067                myContext = theFhirContext;
068        }
069
070        public void setSupportedResourceTypes(Collection<String> theSupportedResourceTypes) {
071                HashSet<String> supportedResourceTypes = new HashSet<>();
072                if (theSupportedResourceTypes != null) {
073                        supportedResourceTypes.addAll(theSupportedResourceTypes);
074                }
075                mySupportedResourceTypes = supportedResourceTypes;
076                myResourceNameToResourceDao = null;
077
078        }
079
080        @Override
081        public void setApplicationContext(ApplicationContext theApplicationContext) throws BeansException {
082                myAppCtx = theApplicationContext;
083        }
084        public IFhirSystemDao getSystemDao() {
085                IFhirSystemDao retVal = mySystemDao;
086                if (retVal == null) {
087                        retVal = myAppCtx.getBean(IFhirSystemDao.class);
088                        mySystemDao = retVal;
089                }
090                return retVal;
091        }
092
093        /**
094         * @throws InvalidRequestException If the given resource type is not supported
095         */
096        public IFhirResourceDao getResourceDao(String theResourceName) {
097                IFhirResourceDao<IBaseResource> retVal = getResourceDaoOrNull(theResourceName);
098                if (retVal == null) {
099                        List<String> supportedResourceTypes = myResourceNameToResourceDao
100                                .keySet()
101                                .stream()
102                                .sorted()
103                                .collect(Collectors.toList());
104                        throw new InvalidRequestException("Unable to process request, this server does not know how to handle resources of type " + theResourceName + " - Can handle: " + supportedResourceTypes);
105                }
106                return retVal;
107        }
108
109        public <R extends IBaseResource> IFhirResourceDao<R> getResourceDao(Class<R> theResourceType) {
110                IFhirResourceDao<R> retVal = getResourceDaoIfExists(theResourceType);
111                Validate.notNull(retVal, "No DAO exists for resource type %s - Have: %s", theResourceType, myResourceNameToResourceDao);
112                return retVal;
113        }
114
115        /**
116         * Use getResourceDaoOrNull
117         */
118        @Deprecated
119        public <T extends IBaseResource> IFhirResourceDao<T> getResourceDaoIfExists(Class<T> theResourceType) {
120                return getResourceDaoOrNull(theResourceType);
121        }
122
123        @Nullable
124        public <T extends IBaseResource> IFhirResourceDao<T> getResourceDaoOrNull(Class<T> theResourceType) {
125                String resourceName = myContext.getResourceType(theResourceType);
126                try {
127                        return (IFhirResourceDao<T>) getResourceDao(resourceName);
128                } catch (InvalidRequestException e) {
129                        return null;
130                }
131        }
132
133        /**
134         * Use getResourceDaoOrNull
135         */
136        @Deprecated
137        public <T extends IBaseResource> IFhirResourceDao<T> getResourceDaoIfExists(String theResourceType) {
138                return getResourceDaoOrNull(theResourceType);
139        }
140
141        @Nullable
142        public <T extends IBaseResource> IFhirResourceDao<T> getResourceDaoOrNull(String theResourceName) {
143                init();
144                return (IFhirResourceDao<T>) myResourceNameToResourceDao.get(theResourceName);
145        }
146
147        @Override
148        public boolean isResourceTypeSupported(String theResourceType) {
149                if (mySupportedResourceTypes == null) {
150                        return getResourceDaoOrNull(theResourceType) != null;
151                }
152                return mySupportedResourceTypes.contains(theResourceType);
153        }
154
155        private void init() {
156                if (myResourceNameToResourceDao != null && !myResourceNameToResourceDao.isEmpty()) {
157                        return;
158                }
159
160                Map<String, IFhirResourceDao> resourceDaos = myAppCtx.getBeansOfType(IFhirResourceDao.class);
161
162                initializeMaps(resourceDaos.values());
163        }
164
165        private void initializeMaps(Collection<IFhirResourceDao> theResourceDaos) {
166
167                myResourceNameToResourceDao = new HashMap<>();
168
169                for (IFhirResourceDao nextResourceDao : theResourceDaos) {
170                        Class resourceType = nextResourceDao.getResourceType();
171                        assert resourceType != null;
172                        RuntimeResourceDefinition nextResourceDef = myContext.getResourceDefinition(resourceType);
173                        if (mySupportedResourceTypes == null || mySupportedResourceTypes.contains(nextResourceDef.getName())) {
174                                myResourceNameToResourceDao.put(nextResourceDef.getName(), nextResourceDao);
175                        }
176                }
177        }
178
179        public void register(IFhirResourceDao theResourceDao) {
180                RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(theResourceDao.getResourceType());
181                String resourceName = resourceDef.getName();
182                myResourceNameToResourceDao.put(resourceName, theResourceDao);
183        }
184
185        public IFhirResourceDao getDaoOrThrowException(Class<? extends IBaseResource> theClass) {
186                IFhirResourceDao retVal = getResourceDao(theClass);
187                if (retVal == null) {
188                        List<String> supportedResourceNames = myResourceNameToResourceDao
189                                .keySet()
190                                .stream()
191                                .map(t -> myContext.getResourceType(t))
192                                .sorted()
193                                .collect(Collectors.toList());
194                        throw new InvalidRequestException("Unable to process request, this server does not know how to handle resources of type " + myContext.getResourceType(theClass) + " - Can handle: " + supportedResourceNames);
195                }
196                return retVal;
197        }
198
199        public void setResourceDaos(Collection<IFhirResourceDao> theResourceDaos) {
200                initializeMaps(theResourceDaos);
201        }
202
203        public IFhirResourceDao getSubscriptionDao() {
204                return getResourceDao(ResourceTypeEnum.SUBSCRIPTION.getCode());
205        }
206
207        public void setSupportedResourceTypes(String... theResourceTypes) {
208                setSupportedResourceTypes(toCollection(theResourceTypes));
209        }
210
211        private List<String> toCollection(String[] theResourceTypes) {
212                List<String> retVal = null;
213                if (theResourceTypes != null && theResourceTypes.length > 0) {
214                        retVal = Arrays.asList(theResourceTypes);
215                }
216                return retVal;
217        }
218
219        public Set<String> getRegisteredDaoTypes() {
220                return Collections.unmodifiableSet(myResourceNameToResourceDao.keySet());
221        }
222}