001package ca.uhn.fhir.jpa.search.reindex;
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.context.FhirContext;
025import ca.uhn.fhir.context.RuntimeResourceDefinition;
026import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
027import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
028import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
029import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
030import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
031import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
032import ca.uhn.fhir.jpa.model.entity.ForcedId;
033import ca.uhn.fhir.jpa.model.entity.ResourceTable;
034import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
035import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
036import org.hl7.fhir.instance.model.api.IBaseResource;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039import org.springframework.beans.factory.annotation.Autowired;
040import org.springframework.stereotype.Service;
041
042import static org.apache.commons.lang3.StringUtils.isBlank;
043
044@Service
045public class ResourceReindexer {
046        private static final Logger ourLog = LoggerFactory.getLogger(ResourceReindexer.class);
047        @Autowired
048        private IResourceHistoryTableDao myResourceHistoryTableDao;
049        @Autowired
050        private IForcedIdDao myForcedIdDao;
051        @Autowired
052        private IResourceTableDao myResourceTableDao;
053        @Autowired
054        private DaoRegistry myDaoRegistry;
055        @Autowired(required = false)
056        private IFulltextSearchSvc myFulltextSearchSvc;
057
058        private final FhirContext myFhirContext;
059
060        public ResourceReindexer(FhirContext theFhirContext) {
061                myFhirContext = theFhirContext;
062        }
063
064        public void readAndReindexResourceByPid(Long theResourcePid) {
065                ResourceTable resourceTable = myResourceTableDao.findById(theResourcePid).orElseThrow(IllegalStateException::new);
066                reindexResourceEntity(resourceTable);
067        }
068
069        public void reindexResourceEntity(ResourceTable theResourceTable) {
070                /*
071                 * This part is because from HAPI 1.5 - 1.6 we changed the format of forced ID to be "type/id" instead of just "id"
072                 */
073                ForcedId forcedId = theResourceTable.getForcedId();
074                if (forcedId != null) {
075                        if (isBlank(forcedId.getResourceType())) {
076                                ourLog.info("Updating resource {} forcedId type to {}", forcedId.getForcedId(), theResourceTable.getResourceType());
077                                forcedId.setResourceType(theResourceTable.getResourceType());
078                                myForcedIdDao.save(forcedId);
079                        }
080                }
081
082                IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(theResourceTable.getResourceType());
083                long expectedVersion = theResourceTable.getVersion();
084                IBaseResource resource = dao.readByPid(new ResourcePersistentId(theResourceTable.getId()), true);
085
086                if (resource == null) {
087                        throw new InternalErrorException(Msg.code(1171) + "Could not find resource version " + theResourceTable.getIdDt().toUnqualified().getValue() + " in database");
088                }
089
090                Long actualVersion = resource.getIdElement().getVersionIdPartAsLong();
091                if (actualVersion < expectedVersion) {
092                        ourLog.warn("Resource {} version {} does not exist, renumbering version {}", resource.getIdElement().toUnqualifiedVersionless().getValue(), resource.getIdElement().getVersionIdPart(), expectedVersion);
093                        myResourceHistoryTableDao.updateVersion(theResourceTable.getId(), actualVersion, expectedVersion);
094                }
095
096                doReindex(theResourceTable, resource);
097        }
098
099        @SuppressWarnings("unchecked")
100        <T extends IBaseResource> void doReindex(ResourceTable theResourceTable, T theResource) {
101                RuntimeResourceDefinition resourceDefinition = myFhirContext.getResourceDefinition(theResource.getClass());
102                Class<T> resourceClass = (Class<T>) resourceDefinition.getImplementingClass();
103                final IFhirResourceDao<T> dao = myDaoRegistry.getResourceDao(resourceClass);
104                dao.reindex(theResource, theResourceTable);
105                if (myFulltextSearchSvc != null) {
106                        // update the full-text index, if active.
107                        myFulltextSearchSvc.reindex(theResourceTable);
108                }
109
110        }
111}