001/*- 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2024 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.search.elastic; 021 022import ca.uhn.fhir.context.ConfigurationException; 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; 025import org.apache.commons.lang3.StringUtils; 026import org.hibernate.search.backend.elasticsearch.index.layout.IndexLayoutStrategy; 027import org.hibernate.search.backend.elasticsearch.logging.impl.Log; 028import org.hibernate.search.util.common.logging.impl.LoggerFactory; 029import org.springframework.beans.factory.annotation.Autowired; 030import org.springframework.stereotype.Service; 031 032import java.lang.invoke.MethodHandles; 033import java.util.regex.Matcher; 034import java.util.regex.Pattern; 035 036/** 037 * This class instructs hibernate search on how to create index names for indexed entities. 038 * In our case, we use this class to add an optional prefix to all indices which are created, which can be controlled via 039 * {@link JpaStorageSettings#setHSearchIndexPrefix(String)}. 040 */ 041@Service 042public class IndexNamePrefixLayoutStrategy implements IndexLayoutStrategy { 043 044 @Autowired 045 private JpaStorageSettings myStorageSettings; 046 047 static final Log log = LoggerFactory.make(Log.class, MethodHandles.lookup()); 048 public static final String NAME = "prefix"; 049 public static final Pattern UNIQUE_KEY_EXTRACTION_PATTERN = Pattern.compile("(.*)-\\d{6}"); 050 051 @Override 052 public String createInitialElasticsearchIndexName(String hibernateSearchIndexName) { 053 return addPrefixIfNecessary(hibernateSearchIndexName + "-000001"); 054 } 055 056 @Override 057 public String createWriteAlias(String hibernateSearchIndexName) { 058 return addPrefixIfNecessary(hibernateSearchIndexName + "-write"); 059 } 060 061 @Override 062 public String createReadAlias(String hibernateSearchIndexName) { 063 return addPrefixIfNecessary(hibernateSearchIndexName + "-read"); 064 } 065 066 private String addPrefixIfNecessary(String theCandidateName) { 067 validateStorageSettingsIsPresent(); 068 if (!StringUtils.isBlank(myStorageSettings.getHSearchIndexPrefix())) { 069 return myStorageSettings.getHSearchIndexPrefix() + "-" + theCandidateName; 070 } else { 071 return theCandidateName; 072 } 073 } 074 075 @Override 076 public String extractUniqueKeyFromHibernateSearchIndexName(String hibernateSearchIndexName) { 077 return hibernateSearchIndexName; 078 } 079 080 @Override 081 public String extractUniqueKeyFromElasticsearchIndexName(String elasticsearchIndexName) { 082 Matcher matcher = UNIQUE_KEY_EXTRACTION_PATTERN.matcher(elasticsearchIndexName); 083 if (!matcher.matches()) { 084 throw log.invalidIndexPrimaryName(elasticsearchIndexName, UNIQUE_KEY_EXTRACTION_PATTERN); 085 } else { 086 String candidateUniqueKey = matcher.group(1); 087 return removePrefixIfNecessary(candidateUniqueKey); 088 } 089 } 090 091 private String removePrefixIfNecessary(String theCandidateUniqueKey) { 092 validateStorageSettingsIsPresent(); 093 if (!StringUtils.isBlank(myStorageSettings.getHSearchIndexPrefix())) { 094 return theCandidateUniqueKey.replace(myStorageSettings.getHSearchIndexPrefix() + "-", ""); 095 } else { 096 return theCandidateUniqueKey; 097 } 098 } 099 100 private void validateStorageSettingsIsPresent() { 101 if (myStorageSettings == null) { 102 throw new ConfigurationException( 103 Msg.code(1168) 104 + "While attempting to boot HAPI FHIR, the Hibernate Search bootstrapper failed to find the StorageSettings. This probably means Hibernate Search has been recently upgraded, or somebody modified HapiFhirLocalContainerEntityManagerFactoryBean."); 105 } 106 } 107}