
001/*- 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2025 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.dao; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.jpa.dao.index.DaoResourceLinkResolver; 024import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; 025import ca.uhn.fhir.jpa.model.dao.JpaPid; 026import ca.uhn.fhir.jpa.model.entity.ResourceTable; 027import ca.uhn.fhir.jpa.search.ResourceSearchUrlSvc; 028import ca.uhn.fhir.util.CanonicalIdentifier; 029import ca.uhn.fhir.util.UrlUtil; 030import jakarta.annotation.Nonnull; 031import org.hl7.fhir.instance.model.api.IBaseReference; 032import org.hl7.fhir.instance.model.api.IBaseResource; 033import org.springframework.beans.factory.annotation.Autowired; 034 035import java.util.List; 036import java.util.stream.Collectors; 037 038import static org.apache.commons.lang3.StringUtils.isNotBlank; 039 040public class JpaDaoResourceLinkResolver extends DaoResourceLinkResolver<JpaPid> { 041 042 @Autowired 043 private FhirContext myFhirContext; 044 045 @Autowired 046 private ResourceSearchUrlSvc myResourceSearchUrlSvc; 047 048 @Override 049 protected void verifyPlaceholderCanBeCreated( 050 Class<? extends IBaseResource> theType, 051 String theIdToAssignToPlaceholder, 052 IBaseReference theReference, 053 IBasePersistedResource theStoredEntity) { 054 if (isNotBlank(theIdToAssignToPlaceholder)) { 055 return; 056 } 057 058 /* 059 * If we're about to create a placeholder resource to satisfy a conditional URL 060 * with identifiers, add an entry in the HFJ_RES_SEARCH_URL table, which is used 061 * to prevent multiple concurrent threads creating the same object as a part of 062 * a conditional create/update. 063 */ 064 String reference = theReference.getReferenceElement().getValue(); 065 if (reference.contains("?")) { 066 String resourceType = myFhirContext.getResourceType(theType); 067 068 List<CanonicalIdentifier> referenceMatchUrlIdentifiers = extractIdentifierFromUrl(reference); 069 String matchUrl = referenceMatchUrlIdentifiers.stream() 070 .map(JpaDaoResourceLinkResolver::toUrlParam) 071 .collect(Collectors.joining("&")); 072 073 myResourceSearchUrlSvc.enforceMatchUrlResourceUniqueness( 074 resourceType, matchUrl, (ResourceTable) theStoredEntity); 075 } 076 } 077 078 @Nonnull 079 private static String toUrlParam(CanonicalIdentifier t) { 080 return "identifier=" + UrlUtil.escapeUrlParam(t.getSystemElement().getValue()) 081 + "|" 082 + UrlUtil.escapeUrlParam(t.getValueElement().getValue()); 083 } 084}