001package ca.uhn.fhir.jpa.bulk.export.job;
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.context.FhirContext;
024import ca.uhn.fhir.context.RuntimeResourceDefinition;
025import ca.uhn.fhir.jpa.batch.log.Logs;
026import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
027import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
028import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
029import ca.uhn.fhir.rest.param.DateRangeParam;
030import org.slf4j.Logger;
031import org.springframework.batch.item.ItemReader;
032import org.springframework.beans.factory.annotation.Autowired;
033import org.springframework.beans.factory.annotation.Value;
034
035import java.util.ArrayList;
036import java.util.Arrays;
037import java.util.Collections;
038import java.util.Date;
039import java.util.Iterator;
040import java.util.List;
041import java.util.stream.Collectors;
042
043public abstract class BaseBulkItemReader implements ItemReader<List<ResourcePersistentId>> {
044        private static final Logger ourLog = Logs.getBatchTroubleshootingLog();
045
046        @Value("#{stepExecutionContext['resourceType']}")
047        protected String myResourceType;
048        @Value("#{jobParameters['readChunkSize']}")
049        protected Long myReadChunkSize;
050        @Autowired
051        protected FhirContext myContext;
052        @Autowired
053        private MatchUrlService myMatchUrlService;
054
055        private Iterator<ResourcePersistentId> myPidIterator;
056        private RuntimeResourceDefinition myResourceDefinition;
057
058        /**
059         * Generate the list of PIDs of all resources of the given myResourceType, which reference any group member of the given myGroupId.
060         * Store them in a member iterator.
061         */
062        protected void loadResourcePIDs() {
063                //Initialize an array to hold the PIDs of the target resources to be exported.
064                myPidIterator = getResourcePidIterator();
065        }
066
067        protected abstract Iterator<ResourcePersistentId> getResourcePidIterator();
068
069        protected abstract String[] getTypeFilterList();
070
071        protected abstract Date getSinceDate();
072
073        protected abstract String getLogInfoForRead();
074
075        protected List<SearchParameterMap> createSearchParameterMapsForResourceType() {
076                RuntimeResourceDefinition theDef = getResourceDefinition();
077                String[] typeFilters = getTypeFilterList();
078                List<SearchParameterMap> spMaps = null;
079                if (typeFilters != null) {
080                        spMaps = Arrays.stream(typeFilters)
081                                .filter(typeFilter -> typeFilter.startsWith(myResourceType + "?"))
082                                .map(filter -> buildSearchParameterMapForTypeFilter(filter, theDef))
083                                .collect(Collectors.toList());
084                }
085
086                //None of the _typeFilters applied to the current resource type, so just make a simple one.
087                if (spMaps == null || spMaps.isEmpty()) {
088                        SearchParameterMap defaultMap = new SearchParameterMap();
089                        enhanceSearchParameterMapWithCommonParameters(defaultMap);
090                        spMaps = Collections.singletonList(defaultMap);
091                }
092
093                return spMaps;
094        }
095
096        private void enhanceSearchParameterMapWithCommonParameters(SearchParameterMap map) {
097                map.setLoadSynchronous(true);
098                if (getSinceDate() != null) {
099                        map.setLastUpdated(new DateRangeParam(getSinceDate(), null));
100                }
101        }
102
103        public SearchParameterMap buildSearchParameterMapForTypeFilter(String theFilter, RuntimeResourceDefinition theDef) {
104                SearchParameterMap searchParameterMap = myMatchUrlService.translateMatchUrl(theFilter, theDef);
105                enhanceSearchParameterMapWithCommonParameters(searchParameterMap);
106                return searchParameterMap;
107        }
108
109        protected RuntimeResourceDefinition getResourceDefinition() {
110                if (myResourceDefinition == null) {
111                        myResourceDefinition = myContext.getResourceDefinition(myResourceType);
112                }
113                return myResourceDefinition;
114        }
115
116        @Override
117        public List<ResourcePersistentId> read() {
118                ourLog.info(getLogInfoForRead());
119
120                if (myPidIterator == null) {
121                        loadResourcePIDs();
122                }
123
124                int count = 0;
125                List<ResourcePersistentId> outgoing = new ArrayList<>();
126                while (myPidIterator.hasNext() && count < myReadChunkSize) {
127                        outgoing.add(myPidIterator.next());
128                        count += 1;
129                }
130
131                return outgoing.size() == 0 ? null : outgoing;
132        }
133}