diff --git a/.github/actions/build-cache/action.yml b/.github/actions/build-cache/action.yml index b2cbf9bfdc1c..0e054c722269 100644 --- a/.github/actions/build-cache/action.yml +++ b/.github/actions/build-cache/action.yml @@ -14,7 +14,7 @@ runs: using: "composite" steps: - name: Set up JDK ${{ inputs.java-version }} - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: ${{ inputs.java-version }} distribution: "temurin" diff --git a/.github/actions/build-module/action.yml b/.github/actions/build-module/action.yml index c1e58f8bc5e1..5644fe6ae426 100644 --- a/.github/actions/build-module/action.yml +++ b/.github/actions/build-module/action.yml @@ -15,12 +15,12 @@ runs: using: "composite" steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1 - name: Set up Java 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '17' diff --git a/.github/actions/generate-module-list/action.yml b/.github/actions/generate-module-list/action.yml index 497ed3ced0ce..2851d83442e0 100644 --- a/.github/actions/generate-module-list/action.yml +++ b/.github/actions/generate-module-list/action.yml @@ -13,10 +13,10 @@ runs: using: 'composite' steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'adopt' java-version: '17' diff --git a/.github/actions/run-tests-and-archive-results/action.yml b/.github/actions/run-tests-and-archive-results/action.yml index 9bb968488091..4bbec0273bbb 100644 --- a/.github/actions/run-tests-and-archive-results/action.yml +++ b/.github/actions/run-tests-and-archive-results/action.yml @@ -18,7 +18,7 @@ runs: using: "composite" steps: - name: Set up JDK ${{ inputs.java-version }} - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: ${{ inputs.java-version }} distribution: 'temurin' diff --git a/.github/workflows/spotless.yml b/.github/workflows/spotless.yml index ef21114c4b3f..51ca4141a876 100644 --- a/.github/workflows/spotless.yml +++ b/.github/workflows/spotless.yml @@ -11,9 +11,9 @@ jobs: permissions: pull-requests: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' diff --git a/hapi-fhir-android/.gitignore b/hapi-fhir-android/.gitignore index e69de29bb2d1..8b137891791f 100644 --- a/hapi-fhir-android/.gitignore +++ b/hapi-fhir-android/.gitignore @@ -0,0 +1 @@ + diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java index af4633bc8afe..03f8b2424e84 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java @@ -463,8 +463,8 @@ private void scanResourceForSearchParams( @SuppressWarnings("JavadocLinkAsPlainText") private String toCanonicalSearchParameterUri(RuntimeResourceDefinition theResourceDef, String theName) { return switch (theName) { - // Hard-code a few URLs that we know don't follow the - // usual pattern + // Hard-code a few URLs that we know don't follow the + // usual pattern case Constants.PARAM_LANGUAGE -> Constants.PARAM_LANGUAGE_URL; case Constants.PARAM_TEXT -> Constants.PARAM_TEXT_URL; case Constants.PARAM_CONTENT -> Constants.PARAM_CONTENT_URL; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IEntityResult.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IEntityResult.java index fd29eea6f32a..2ec2be471f94 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IEntityResult.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IEntityResult.java @@ -19,7 +19,7 @@ */ package ca.uhn.fhir.rest.gclient; -import org.checkerframework.checker.nullness.qual.NonNull; +import org.jspecify.annotations.NonNull; import java.io.InputStream; import java.util.List; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java index 1d60108710cf..6265c39c9a44 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java @@ -490,10 +490,10 @@ public static String sanitizeUrlPart(CharSequence theString) { char nextChar = theString.charAt(j); switch (nextChar) { - /* - * NB: If you add a constant here, you also need to add it - * to isNeedsSanitization()!! - */ + /* + * NB: If you add a constant here, you also need to add it + * to isNeedsSanitization()!! + */ case '\'': buffer.append("'"); break; diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseCommand.java index 5dc1bef805ba..1d469d7513a3 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseCommand.java @@ -55,7 +55,6 @@ import org.hl7.fhir.instance.model.api.IBaseBundle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.util.Base64Utils; import java.io.BufferedReader; import java.io.Console; @@ -68,6 +67,7 @@ import java.io.InputStreamReader; import java.io.Reader; import java.util.Arrays; +import java.util.Base64; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -363,7 +363,7 @@ protected String getAndParseOptionBasicAuthHeader(CommandLine theCommandLine, St } byte[] basicAuth = optionValue.getBytes(); - String base64EncodedBasicAuth = Base64Utils.encodeToString(basicAuth); + String base64EncodedBasicAuth = Base64.getEncoder().encodeToString(basicAuth); basicAuthHeaderValue = Constants.HEADER_AUTHORIZATION_VALPREFIX_BASIC + base64EncodedBasicAuth; } return basicAuthHeaderValue; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java index 1dde63d673aa..51e908a4c444 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java @@ -139,7 +139,6 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; -import org.checkerframework.checker.nullness.qual.NonNull; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseBundle; @@ -151,9 +150,11 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.jspecify.annotations.NonNull; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -1424,8 +1425,7 @@ private void addParam(String theName, IBase theValue) { } private void addParam(String theName, IQueryParameterType theValue) { - IPrimitiveType stringType = - ParametersUtil.createString(myContext, theValue.getValueAsQueryToken(myContext)); + IPrimitiveType stringType = ParametersUtil.createString(myContext, theValue.getValueAsQueryToken()); addParam(theName, stringType); } @@ -2206,11 +2206,11 @@ public OUTPUT execute() { Map> params = getParamMap(); for (TokenParam next : myTags) { - addParam(params, Constants.PARAM_TAG, next.getValueAsQueryToken(myContext)); + addParam(params, Constants.PARAM_TAG, next.getValueAsQueryToken()); } for (TokenParam next : mySecurity) { - addParam(params, Constants.PARAM_SECURITY, next.getValueAsQueryToken(myContext)); + addParam(params, Constants.PARAM_SECURITY, next.getValueAsQueryToken()); } for (Collection profileUris : myProfiles) { @@ -2281,7 +2281,7 @@ public OUTPUT execute() { if (myLastUpdated != null) { for (DateParam next : myLastUpdated.getValuesAsQueryTokens()) { - addParam(params, Constants.PARAM_LASTUPDATED, next.getValueAsQueryToken(myContext)); + addParam(params, Constants.PARAM_LASTUPDATED, next.getValueAsQueryToken()); } } @@ -2504,7 +2504,7 @@ public StringOutcome invokeClient( Map> theHeaders) throws IOException, BaseServerResponseException { - String payload = IOUtils.toString(theResponseInputStream, Charsets.UTF_8); + String payload = IOUtils.toString(theResponseInputStream, StandardCharsets.UTF_8); return new StringOutcome(theResponseStatusCode, payload, theHeaders); } } diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirLocalContainerEntityManagerFactoryBean.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirLocalContainerEntityManagerFactoryBean.java index 8a5ee1583108..e8238d2e0398 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirLocalContainerEntityManagerFactoryBean.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirLocalContainerEntityManagerFactoryBean.java @@ -90,9 +90,6 @@ public Map getJpaPropertyMap() { retVal.put(BatchSettings.ORDER_UPDATES, "true"); } - if (!retVal.containsKey(BatchSettings.BATCH_VERSIONED_DATA)) { - retVal.put(BatchSettings.BATCH_VERSIONED_DATA, "true"); - } // Why is this here, you ask? LocalContainerEntityManagerFactoryBean actually clobbers the setting hibernate // needs in order to be able to resolve beans, so we add it back in manually here if (!retVal.containsKey(ManagedBeanSettings.BEAN_CONTAINER)) { diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirDerbyDialect.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirDerbyDialect.java index d554650c6cb4..d1324b88ba4d 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirDerbyDialect.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirDerbyDialect.java @@ -20,12 +20,12 @@ package ca.uhn.fhir.jpa.model.dialect; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; -import org.hibernate.dialect.DerbyDialect; +import org.hibernate.community.dialect.DerbyDialect; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; /** * Dialect for Derby database. - * Minimum version: 10.14.2 + * Minimum version: 10.15.2 */ public class HapiFhirDerbyDialect extends DerbyDialect implements IHapiFhirDialect { diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseHapiScheduler.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseHapiScheduler.java index fd3a429a0d9e..b629a18e34b0 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseHapiScheduler.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseHapiScheduler.java @@ -280,7 +280,7 @@ private static class NonConcurrentJobDetailImpl extends JobDetailImpl { // All HAPI FHIR jobs shouldn't allow concurrent execution @Override - public boolean isConcurrentExectionDisallowed() { + public boolean isConcurrentExecutionDisallowed() { return true; } } diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/DerbyTenSevenHapiFhirDialect.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/DerbyTenSevenHapiFhirDialect.java index 55e1c0a288ec..46cf81221baa 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/DerbyTenSevenHapiFhirDialect.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/DerbyTenSevenHapiFhirDialect.java @@ -19,7 +19,7 @@ */ package ca.uhn.fhir.jpa.util; -import org.hibernate.dialect.DerbyDialect; +import org.hibernate.community.dialect.DerbyDialect; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; import org.slf4j.Logger; diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index cbb4326cfdab..31b3ff00c82c 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -520,29 +520,42 @@ ca.uhn.hapi.fhir hapi-tinder-plugin ${project.version} - generate-ddl-legacy - process-classes - - generate-ddl - - - false - ${project.build.directory}/classes/ca/uhn/hapi/fhir/jpa/docs/database/nonpartitioned - - - - generate-ddl-partitioned - process-classes - - generate-ddl - - - true - ${project.build.directory}/classes/ca/uhn/hapi/fhir/jpa/docs/database/partitioned - - + + + generate-ddl-legacy + process-classes + + generate-ddl + + + false + ${project.build.directory}/classes/ca/uhn/hapi/fhir/jpa/docs/database/nonpartitioned + + + + generate-ddl-partitioned + process-classes + + generate-ddl + + + true + ${project.build.directory}/classes/ca/uhn/hapi/fhir/jpa/docs/database/partitioned + + + + + + + + + + + + + ca.uhn.fhir.jpa.entity ca.uhn.fhir.jpa.model.entity diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java index 15a3952586f8..0bce55589cba 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java @@ -194,7 +194,7 @@ public ISearchQueryExecutor searchScrolled( String theResourceType, SearchParameterMap theParams, RequestDetails theRequestDetails) { validateHibernateSearchIsEnabled(); - SearchQueryOptionsStep searchQueryOptionsStep = + SearchQueryOptionsStep searchQueryOptionsStep = getSearchQueryOptionsStep(theResourceType, theParams, null); logQuery(searchQueryOptionsStep, theRequestDetails); @@ -214,7 +214,7 @@ private ISearchQueryExecutor doSearch( int count = getMaxFetchSize(theParams, theMaxResultsToFetch); // perform an offset search instead of a scroll one, which doesn't allow for offset - SearchQueryOptionsStep searchQueryOptionsStep = + SearchQueryOptionsStep searchQueryOptionsStep = getSearchQueryOptionsStep(theResourceType, theParams, theReferencingPid); logQuery(searchQueryOptionsStep, theRequestDetails); List longs = searchQueryOptionsStep.fetchHits(offset, count); @@ -238,11 +238,11 @@ private int getMaxFetchSize(SearchParameterMap theParams, Integer theMax) { } @SuppressWarnings("rawtypes") - private SearchQueryOptionsStep getSearchQueryOptionsStep( + private SearchQueryOptionsStep getSearchQueryOptionsStep( String theResourceType, SearchParameterMap theParams, IResourcePersistentId theReferencingPid) { dispatchEvent(IHSearchEventListener.HSearchEventType.SEARCH); - SearchQueryOptionsStep query = getSearchSession() + SearchQueryOptionsStep query = getSearchSession() .search(ResourceTable.class) // The document id is the PK which is pid. We use this instead of _myId to avoid fetching the doc body. .select( @@ -536,7 +536,7 @@ private CompositeProjectionOptionsStep bui @Override public long count(String theResourceName, SearchParameterMap theParams) { - SearchQueryOptionsStep queryOptionsStep = + SearchQueryOptionsStep queryOptionsStep = getSearchQueryOptionsStep(theResourceName, theParams, null); return queryOptionsStep.fetchTotalHitCount(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchClauseBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchClauseBuilder.java index aa439565b01a..0fd29852f8b1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchClauseBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchClauseBuilder.java @@ -1,22 +1,3 @@ -/*- - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2025 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ package ca.uhn.fhir.jpa.dao.search; import ca.uhn.fhir.context.FhirContext; @@ -25,7 +6,6 @@ import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel; import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.model.util.UcumServiceUtil; -import ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.rest.api.Constants; @@ -52,9 +32,7 @@ import org.hibernate.search.engine.search.common.BooleanOperator; import org.hibernate.search.engine.search.predicate.dsl.BooleanPredicateClausesStep; import org.hibernate.search.engine.search.predicate.dsl.PredicateFinalStep; -import org.hibernate.search.engine.search.predicate.dsl.RangePredicateOptionsStep; import org.hibernate.search.engine.search.predicate.dsl.SearchPredicateFactory; -import org.hibernate.search.engine.search.predicate.dsl.WildcardPredicateOptionsStep; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -96,7 +74,8 @@ public class ExtendedHSearchClauseBuilder { public static final String PATH_JOINER = "."; final FhirContext myFhirContext; - public final BooleanPredicateClausesStep myRootClause; + // Raw type to avoid HS 7.x generic arity changes + public final BooleanPredicateClausesStep myRootClause; public final StorageSettings myStorageSettings; final PathContext myRootContext; @@ -106,7 +85,8 @@ public class ExtendedHSearchClauseBuilder { public ExtendedHSearchClauseBuilder( FhirContext myFhirContext, StorageSettings theStorageSettings, - BooleanPredicateClausesStep theRootClause, + // Raw type to be compatible with HS 7.1.x + BooleanPredicateClausesStep theRootClause, SearchPredicateFactory thePredicateFactory) { this.myFhirContext = myFhirContext; this.myStorageSettings = theStorageSettings; @@ -163,14 +143,6 @@ private String getTrimmedStringValue(IQueryParameterType nextOr) { return StringUtils.defaultString(value).trim(); } - /** - * String Search params are valid, so are two special params, _content and _text. - * - * @param theSearchParamName The name of the SP - * @param nextOr the or values of the query parameter. - * - * @return a boolean indicating whether we can treat this as a string. - */ private static boolean isStringParamOrEquivalent(String theSearchParamName, IQueryParameterType nextOr) { List specialSearchParamsToTreatAsStrings = List.of(Constants.PARAM_TEXT, Constants.PARAM_CONTENT); return (nextOr instanceof StringParam) @@ -223,10 +195,8 @@ private PredicateFinalStep buildTokenUnmodifiedMatchOn(IQueryParameterType orTer .matching(token.getValueAsQueryToken(this.myFhirContext)); } } else if (orTerm instanceof StringParam) { - // MB I don't quite understand why FhirResourceDaoR4SearchNoFtTest.testSearchByIdParamWrongType() uses - // String but here we are - StringParam string = (StringParam) orTerm; // treat a string as a code with no system (like _id) + StringParam string = (StringParam) orTerm; return thePathContext .match() .field(joinPath(pathPrefix, INDEX_TYPE_TOKEN, TOKEN_CODE)) @@ -243,8 +213,6 @@ public void addStringTextSearch(String theSearchParamName, List nextOrList, String fieldName) { Set orTerms = TermHelper.makePrefixSearchTerm(extractOrStringParams(theSearchParamName, nextOrList)); @@ -284,24 +245,12 @@ private void addSimpleQueryMatchClauses( .simpleQueryString() .field(fieldName) .matching(query) - .defaultOperator( - BooleanOperator.AND)); // term value may contain multiple tokens. Require all of them to - // be - // present. - + .defaultOperator(BooleanOperator.AND)); } else { ourLog.warn("No Terms found in query parameter {}", nextOrList); } } - /** - * Note that this `match()` operation is different from out standard behaviour, which uses simpleQueryString(). This `match()` forces a precise string match, Whereas `simpleQueryString()` uses a more nebulous - * and loose check against a collection of terms. We only use this when we see ` _text:contains=` or `_content:contains=` search. - * - * @param theSearchParamName the Name of the search parameter - * @param nextOrList the list of query parameters - * @param fieldName the field name in the index document to compare with. - */ private void addPreciseMatchClauses( String theSearchParamName, List nextOrList, String fieldName) { Set orTerms = TermHelper.makePrefixSearchTerm(extractOrStringParams(theSearchParamName, nextOrList)); @@ -331,7 +280,6 @@ public void addStringContainsSearch( Set terms = extractOrStringParams(theSearchParamName, nextAnd); ourLog.debug("addStringContainsSearch {} {}", theSearchParamName, terms); List orTerms = terms.stream() - // wildcard is a term-level query, so queries aren't analyzed. Do our own normalization first. .map(this::normalize) .map(s -> myRootContext.wildcard().field(fieldPath).matching("*" + s + "*")) .collect(Collectors.toList()); @@ -340,13 +288,6 @@ public void addStringContainsSearch( } } - /** - * Normalize the string to match our standardAnalyzer. - * @see HapiHSearchAnalysisConfigurers.HapiLuceneAnalysisConfigurer#STANDARD_ANALYZER - * - * @param theString the raw string - * @return a case and accent normalized version of the input - */ @Nonnull private String normalize(String theString) { return StringUtil.normalizeStringForSearchIndexing(theString).toLowerCase(Locale.ROOT); @@ -366,7 +307,8 @@ public void addStringUnmodifiedSearch( } } - private WildcardPredicateOptionsStep buildStringUnmodifiedClause(String theString, PathContext theContext) { + // Return PredicateFinalStep instead of WildcardPredicateOptionsStep + private PredicateFinalStep buildStringUnmodifiedClause(String theString, PathContext theContext) { return theContext .wildcard() .field(joinPath(theContext.getContextPath(), INDEX_TYPE_STRING, IDX_STRING_NORMALIZED)) @@ -390,81 +332,6 @@ public void addReferenceUnchainedSearch( } } - /** - * Create date clause from date params. The date lower and upper bounds are taken - * into consideration when generating date query ranges - * - *

Example 1 ('eq' prefix/empty): http://fhirserver/Observation?date=eq2020 - * would generate the following search clause - *

-	 * {@code
-	 * {
-	 *  "bool": {
-	 *    "must": [{
-	 *      "range": {
-	 *        "sp.date.dt.lower-ord": { "gte": "20200101" }
-	 *      }
-	 *    }, {
-	 *      "range": {
-	 *        "sp.date.dt.upper-ord": { "lte": "20201231" }
-	 *      }
-	 *    }]
-	 *  }
-	 * }
-	 * }
-	 * 
- * - *

Example 2 ('gt' prefix): http://fhirserver/Observation?date=gt2020-01-01T08:00:00.000 - *

No timezone in the query will be taken as localdatetime(for e.g MST/UTC-07:00 in this case) converted to UTC before comparison

- *
-	 * {@code
-	 * {
-	 *   "range":{
-	 *     "sp.date.dt.upper":{ "gt": "2020-01-01T15:00:00.000000000Z" }
-	 *   }
-	 * }
-	 * }
-	 * 
- * - *

Example 3 between dates: {@code http://fhirserver/Observation?date=ge2010-01-01&date=le2020-01}

- *
-	 * {@code
-	 * {
-	 *   "range":{
-	 *     "sp.date.dt.upper-ord":{ "gte":"20100101" }
-	 *   },
-	 *   "range":{
-	 *     "sp.date.dt.lower-ord":{ "lte":"20200101" }
-	 *   }
-	 * }
-	 * }
-	 * 
- * - *

Example 4 not equal: {@code http://fhirserver/Observation?date=ne2021}

- *
-	 * {@code
-	 * {
-	 *    "bool": {
-	 *       "should": [{
-	 *          "range": {
-	 *             "sp.date.dt.upper-ord": { "lt": "20210101" }
-	 *          }
-	 *       }, {
-	 *          "range": {
-	 *             "sp.date.dt.lower-ord": { "gt": "20211231" }
-	 *          }
-	 *       }],
-	 *       "minimum_should_match": "1"
-	 *    }
-	 * }
-	 * }
-	 * 
- * - * @param theSearchParamName e.g code - * @param theDateAndOrTerms The and/or list of DateParam values - * - * buildDateTermClause(subComponentPath, value); - */ public void addDateUnmodifiedSearch(String theSearchParamName, List> theDateAndOrTerms) { for (List nextOrList : theDateAndOrTerms) { @@ -494,10 +361,8 @@ private PredicateFinalStep generateDateOrdinalSearchTerms(DateParam theDateParam int upperBoundAsOrdinal; ParamPrefixEnum prefix = theDateParam.getPrefix(); - // default when handling 'Day' temporal types lowerBoundAsOrdinal = upperBoundAsOrdinal = DateUtils.convertDateToDayInteger(theDateParam.getValue()); TemporalPrecisionEnum precision = theDateParam.getPrecision(); - // complete the date from 'YYYY' and 'YYYY-MM' temporal types if (precision == TemporalPrecisionEnum.YEAR || precision == TemporalPrecisionEnum.MONTH) { Pair completedDate = DateUtils.getCompletedDate(theDateParam.getValueAsString()); lowerBoundAsOrdinal = Integer.parseInt(completedDate.getLeft().replace("-", "")); @@ -505,20 +370,18 @@ private PredicateFinalStep generateDateOrdinalSearchTerms(DateParam theDateParam } if (Objects.isNull(prefix) || prefix == ParamPrefixEnum.EQUAL) { - // For equality prefix we would like the date to fall between the lower and upper bound List predicateSteps = Arrays.asList( theSpContext.range().field(lowerOrdinalField).atLeast(lowerBoundAsOrdinal), theSpContext.range().field(upperOrdinalField).atMost(upperBoundAsOrdinal)); - BooleanPredicateClausesStep booleanStep = theSpContext.bool(); + // Raw type to absorb HS 7.x generic changes + BooleanPredicateClausesStep booleanStep = theSpContext.bool(); predicateSteps.forEach(booleanStep::must); return booleanStep; } else if (ParamPrefixEnum.GREATERTHAN == prefix || ParamPrefixEnum.STARTS_AFTER == prefix) { - // TODO JB: more fine tuning needed for STARTS_AFTER return theSpContext.range().field(upperOrdinalField).greaterThan(upperBoundAsOrdinal); } else if (ParamPrefixEnum.GREATERTHAN_OR_EQUALS == prefix) { return theSpContext.range().field(upperOrdinalField).atLeast(upperBoundAsOrdinal); } else if (ParamPrefixEnum.LESSTHAN == prefix || ParamPrefixEnum.ENDS_BEFORE == prefix) { - // TODO JB: more fine tuning needed for END_BEFORE return theSpContext.range().field(lowerOrdinalField).lessThan(lowerBoundAsOrdinal); } else if (ParamPrefixEnum.LESSTHAN_OR_EQUALS == prefix) { return theSpContext.range().field(lowerOrdinalField).atMost(lowerBoundAsOrdinal); @@ -526,7 +389,7 @@ private PredicateFinalStep generateDateOrdinalSearchTerms(DateParam theDateParam List predicateSteps = Arrays.asList( theSpContext.range().field(upperOrdinalField).lessThan(lowerBoundAsOrdinal), theSpContext.range().field(lowerOrdinalField).greaterThan(upperBoundAsOrdinal)); - BooleanPredicateClausesStep booleanStep = theSpContext.bool(); + BooleanPredicateClausesStep booleanStep = theSpContext.bool(); predicateSteps.forEach(booleanStep::should); booleanStep.minimumShouldMatchNumber(1); return booleanStep; @@ -543,15 +406,20 @@ private PredicateFinalStep generateDateInstantSearchTerms(DateParam theDateParam if (ParamPrefixEnum.NOT_EQUAL == prefix) { Instant dateInstant = theDateParam.getValue().toInstant(); List predicateSteps = Arrays.asList( - theSpContext.range().field(upperInstantField).lessThan(dateInstant), - theSpContext.range().field(lowerInstantField).greaterThan(dateInstant)); - BooleanPredicateClausesStep booleanStep = theSpContext.bool(); + ((SearchPredicateFactory) theSpContext) + .range() + .field(upperInstantField) + .lessThan(dateInstant), + ((SearchPredicateFactory) theSpContext) + .range() + .field(lowerInstantField) + .greaterThan(dateInstant)); + BooleanPredicateClausesStep booleanStep = ((SearchPredicateFactory) theSpContext).bool(); predicateSteps.forEach(booleanStep::should); booleanStep.minimumShouldMatchNumber(1); return booleanStep; } - // Consider lower and upper bounds for building range predicates DateRangeParam dateRange = new DateRangeParam(theDateParam); Instant lowerBoundAsInstant = Optional.ofNullable(dateRange.getLowerBound()) .map(param -> param.getValue().toInstant()) @@ -561,7 +429,6 @@ private PredicateFinalStep generateDateInstantSearchTerms(DateParam theDateParam .orElse(null); if (prefix == ParamPrefixEnum.EQUAL) { - // For equality prefix we would like the date to fall between the lower and upper bound List predicateSteps = Arrays.asList( ((SearchPredicateFactory) theSpContext) .range() @@ -571,7 +438,7 @@ private PredicateFinalStep generateDateInstantSearchTerms(DateParam theDateParam .range() .field(upperInstantField) .atMost(upperBoundAsInstant)); - BooleanPredicateClausesStep booleanStep = ((SearchPredicateFactory) theSpContext).bool(); + BooleanPredicateClausesStep booleanStep = ((SearchPredicateFactory) theSpContext).bool(); predicateSteps.forEach(booleanStep::must); return booleanStep; } else if (ParamPrefixEnum.GREATERTHAN == prefix || ParamPrefixEnum.STARTS_AFTER == prefix) { @@ -600,19 +467,10 @@ private PredicateFinalStep generateDateInstantSearchTerms(DateParam theDateParam Msg.code(2256) + "Date search param does not support prefix of type: " + prefix); } - /** - * Differences with DB search: - * _ is not all-normalized-or-all-not. Each parameter is applied on quantity or normalized quantity depending on UCUM fitness - * _ respects ranges for equal and approximate qualifiers - * - * Strategy: For each parameter, if it can be canonicalized, it is, and used against 'normalized-value-quantity' index - * otherwise it is applied as-is to 'value-quantity' - */ public void addQuantityUnmodifiedSearch( String theSearchParamName, List> theQuantityAndOrTerms) { for (List nextOrList : theQuantityAndOrTerms) { - // we build quantity predicates in a nested context so we can match units and systems with values. PredicateFinalStep nestedClause = myRootContext.buildPredicateInNestedContext(theSearchParamName, nextedContext -> { List orClauses = nextOrList.stream() @@ -626,10 +484,10 @@ public void addQuantityUnmodifiedSearch( } } - private BooleanPredicateClausesStep buildQuantityTermClause( + private BooleanPredicateClausesStep buildQuantityTermClause( IQueryParameterType theQueryParameter, PathContext thePathContext) { - BooleanPredicateClausesStep quantityClause = ((SearchPredicateFactory) thePathContext).bool(); + BooleanPredicateClausesStep quantityClause = ((SearchPredicateFactory) thePathContext).bool(); QuantityParam qtyParam = QuantityParam.toQuantityParam(theQueryParameter); ParamPrefixEnum activePrefix = qtyParam.getPrefix() == null ? ParamPrefixEnum.EQUAL : qtyParam.getPrefix(); @@ -672,14 +530,6 @@ private BooleanPredicateClausesStep buildQuantityTermClause( return quantityClause; } - /** - * Shared helper between quantity and number - * @param valueFieldPath The path leading to index node - * @param thePrefix the query prefix (e.g. lt). Null means eq - * @param theNumberValue the query value - * @param thePathContext HSearch builder - * @return a query predicate applying the prefix to the value - */ @Nonnull private PredicateFinalStep buildNumericClause( String valueFieldPath, ParamPrefixEnum thePrefix, BigDecimal theNumberValue, PathContext thePathContext) { @@ -691,7 +541,6 @@ private PredicateFinalStep buildNumericClause( ParamPrefixEnum activePrefix = thePrefix == null ? ParamPrefixEnum.EQUAL : thePrefix; switch (activePrefix) { - // searches for resource quantity between passed param value +/- 10% case APPROXIMATE: predicate = ((SearchPredicateFactory) thePathContext) .range() @@ -699,7 +548,6 @@ private PredicateFinalStep buildNumericClause( .between(value - approxTolerance, value + approxTolerance); break; - // searches for resource quantity between passed param value +/- 5% case EQUAL: predicate = ((SearchPredicateFactory) thePathContext) .range() @@ -707,16 +555,14 @@ private PredicateFinalStep buildNumericClause( .between(range.getLeft().doubleValue(), range.getRight().doubleValue()); break; - // searches for resource quantity > param value case GREATERTHAN: - case STARTS_AFTER: // treated as GREATERTHAN because search doesn't handle ranges + case STARTS_AFTER: predicate = ((SearchPredicateFactory) thePathContext) .range() .field(valueFieldPath) .greaterThan(value); break; - // searches for resource quantity not < param value case GREATERTHAN_OR_EQUALS: predicate = ((SearchPredicateFactory) thePathContext) .range() @@ -724,16 +570,14 @@ private PredicateFinalStep buildNumericClause( .atLeast(value); break; - // searches for resource quantity < param value case LESSTHAN: - case ENDS_BEFORE: // treated as LESSTHAN because search doesn't handle ranges + case ENDS_BEFORE: predicate = ((SearchPredicateFactory) thePathContext) .range() .field(valueFieldPath) .lessThan(value); break; - // searches for resource quantity not > param value case LESSTHAN_OR_EQUALS: predicate = ((SearchPredicateFactory) thePathContext) .range() @@ -741,9 +585,9 @@ private PredicateFinalStep buildNumericClause( .atMost(value); break; - // NOT_EQUAL: searches for resource quantity not between passed param value +/- 5% case NOT_EQUAL: - RangePredicateOptionsStep negRange = ((SearchPredicateFactory) thePathContext) + // Avoid RangePredicateOptionsStep type — keep as PredicateFinalStep for compatibility + PredicateFinalStep negRange = ((SearchPredicateFactory) thePathContext) .range() .field(valueFieldPath) .between(range.getLeft().doubleValue(), range.getRight().doubleValue()); @@ -803,10 +647,6 @@ public void addCompositeUnmodifiedSearch( List> theCompositeAndOrTerms) { for (List nextOrList : theCompositeAndOrTerms) { - // The index data for each extracted element is stored in a separate nested HSearch document. - // Create a nested parent node for all component predicates. - // Each can share this nested beacuse all nested docs share a parent id. - PredicateFinalStep nestedClause = myRootContext.buildPredicateInNestedContext(theSearchParam.getName(), nestedContext -> { List orClauses = nextOrList.stream() @@ -820,14 +660,6 @@ public void addCompositeUnmodifiedSearch( } } - /** - * Compute the match clause for all the components of theCompositeQueryParam. - * - * @param theSearchParam The composite SP - * @param theSubSearchParams the composite component SPs - * @param theCompositeQueryParam the query param values - * @param theCompositeContext the root of the nested SP query. - */ private PredicateFinalStep computeCompositeTermClause( RuntimeSearchParam theSearchParam, List theSubSearchParams, @@ -849,10 +681,7 @@ private PredicateFinalStep computeCompositeTermClause( theSubSearchParams.size(), values.size()); - // The index data for each extracted element is stored in a separate nested HSearch document. - - // Create a nested parent node for all component predicates. - BooleanPredicateClausesStep compositeClause = ((SearchPredicateFactory) theCompositeContext).bool(); + BooleanPredicateClausesStep compositeClause = ((SearchPredicateFactory) theCompositeContext).bool(); for (int i = 0; i < theSubSearchParams.size(); i += 1) { RuntimeSearchParam component = theSubSearchParams.get(i); IQueryParameterType value = values.get(i); @@ -878,7 +707,6 @@ private PredicateFinalStep computeCompositeTermClause( subMatch = buildNumericClause(value, componentContext); break; case REFERENCE: - default: break; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractor.java index d8017b1bfac0..a2fc907f9bf3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractor.java @@ -227,15 +227,15 @@ private void indexTokenValue(ExtendedHSearchIndexData theRetVal, RuntimeSearchPa case "Coding": addToken_Coding(theRetVal, spName, (IBaseCoding) nextValue); break; - // TODO share this with TokenExtractor and introduce a ITokenIndexer interface. - // Ignore unknown types for now. - // This is just for autocomplete, and we are focused on Observation.code, category, combo-code, etc. - // case "Identifier": - // mySearchParamExtractor.addToken_Identifier(myResourceTypeName, params, searchParam, value); - // break; - // case "ContactPoint": - // mySearchParamExtractor.addToken_ContactPoint(myResourceTypeName, params, searchParam, value); - // break; + // TODO share this with TokenExtractor and introduce a ITokenIndexer interface. + // Ignore unknown types for now. + // This is just for autocomplete, and we are focused on Observation.code, category, combo-code, etc. + // case "Identifier": + // mySearchParamExtractor.addToken_Identifier(myResourceTypeName, params, searchParam, value); + // break; + // case "ContactPoint": + // mySearchParamExtractor.addToken_ContactPoint(myResourceTypeName, params, searchParam, value); + // break; default: break; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchSearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchSearchBuilder.java index 613f6d10fe7c..2cf3477ae705 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchSearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchSearchBuilder.java @@ -187,7 +187,7 @@ private boolean isParamTypeSupported(IQueryParameterType param) { } } else if (param instanceof StringParam) { switch (modifier) { - // we support string:text, string:contains, string:exact, and unmodified string. + // we support string:text, string:contains, string:exact, and unmodified string. case Constants.PARAMQUALIFIER_STRING_TEXT: case Constants.PARAMQUALIFIER_STRING_EXACT: case Constants.PARAMQUALIFIER_STRING_CONTAINS: diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/HSearchCompositeSearchIndexDataImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/HSearchCompositeSearchIndexDataImpl.java index 63c7630295fb..de3b62047823 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/HSearchCompositeSearchIndexDataImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/HSearchCompositeSearchIndexDataImpl.java @@ -169,7 +169,7 @@ public void writeIndexEntry(HSearchIndexWriter theHSearchIndexWriter, HSearchEle case REFERENCE: break; - // unsupported + // unsupported case SPECIAL: case HAS: break; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/PathContext.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/PathContext.java index e90c4b5400d6..1bda24eaea8f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/PathContext.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/PathContext.java @@ -1,22 +1,3 @@ -/*- - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2025 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ package ca.uhn.fhir.jpa.dao.search; import jakarta.annotation.Nonnull; @@ -62,17 +43,17 @@ * Holds current query path, boolean clause accumulating AND clauses, and a factory for new predicates. * * The Hibernate Search SearchPredicateFactory is "smart", and knows to wrap references to nested fields - * in a nested clause. This is a problem if we want to accumulate them in a single boolean before nesting. + * in a nested clause. This is a problem if we want to accumulate them in a single boolean before nesting. * Instead, we keep track of the current query path (e.g. "nsp.value-quantity"), and the right SearchPredicateFactory * to use. */ class PathContext implements SearchPredicateFactory { private final String myPathPrefix; - private final BooleanPredicateClausesStep myRootClause; + private final BooleanPredicateClausesStep myRootClause; private final SearchPredicateFactory myPredicateFactory; PathContext( - String thePrefix, BooleanPredicateClausesStep theClause, SearchPredicateFactory thePredicateFactory) { + String thePrefix, BooleanPredicateClausesStep theClause, SearchPredicateFactory thePredicateFactory) { myRootClause = theClause; myPredicateFactory = thePredicateFactory; myPathPrefix = thePrefix; @@ -80,7 +61,7 @@ class PathContext implements SearchPredicateFactory { @Nonnull static PathContext buildRootContext( - BooleanPredicateClausesStep theRootClause, SearchPredicateFactory thePredicateFactory) { + BooleanPredicateClausesStep theRootClause, SearchPredicateFactory thePredicateFactory) { return new PathContext("", theRootClause, thePredicateFactory); } @@ -126,7 +107,7 @@ public PredicateFinalStep orPredicateOrSingle(List if (theOrList.size() == 1) { finalClause = theOrList.get(0); } else { - BooleanPredicateClausesStep orClause = myPredicateFactory.bool(); + BooleanPredicateClausesStep orClause = myPredicateFactory.bool(); orClause.minimumShouldMatchNumber(1); theOrList.forEach(orClause::should); finalClause = orClause; @@ -137,7 +118,7 @@ public PredicateFinalStep orPredicateOrSingle(List // implement SearchPredicateFactory @Override - public MatchAllPredicateOptionsStep matchAll() { + public MatchAllPredicateOptionsStep matchAll() { return myPredicateFactory.matchAll(); } @@ -152,17 +133,17 @@ public MatchIdPredicateMatchingStep id() { } @Override - public BooleanPredicateClausesStep bool() { + public BooleanPredicateClausesStep bool() { return myPredicateFactory.bool(); } @Override - public PredicateFinalStep bool(Consumer> clauseContributor) { + public PredicateFinalStep bool(Consumer> clauseContributor) { return myPredicateFactory.bool(clauseContributor); } @Override - public SimpleBooleanPredicateClausesStep and() { + public SimpleBooleanPredicateClausesStep and() { return myPredicateFactory.and(); } @@ -179,7 +160,7 @@ public SimpleBooleanPredicateOptionsStep and( } @Override - public SimpleBooleanPredicateClausesStep or() { + public SimpleBooleanPredicateClausesStep or() { return myPredicateFactory.or(); } @@ -206,62 +187,66 @@ public NotPredicateFinalStep not(PredicateFinalStep thePredicateFinalStep) { } @Override - public MatchPredicateFieldStep match() { + public MatchPredicateFieldStep match() { return myPredicateFactory.match(); } @Override - public RangePredicateFieldStep range() { + public RangePredicateFieldStep range() { return myPredicateFactory.range(); } @Override - public PhrasePredicateFieldStep phrase() { + public PhrasePredicateFieldStep phrase() { return myPredicateFactory.phrase(); } @Override - public WildcardPredicateFieldStep wildcard() { + public WildcardPredicateFieldStep wildcard() { return myPredicateFactory.wildcard(); } @Override - public PrefixPredicateFieldStep prefix() { + public PrefixPredicateFieldStep prefix() { return myPredicateFactory.prefix(); } @Override - public RegexpPredicateFieldStep regexp() { + public RegexpPredicateFieldStep regexp() { return myPredicateFactory.regexp(); } @Override - public TermsPredicateFieldStep terms() { + public TermsPredicateFieldStep terms() { return myPredicateFactory.terms(); } + /** + * @deprecated use {@link #nested(String)} instead + */ + @Deprecated(since = "8.6.0", forRemoval = true) @Override - public NestedPredicateFieldStep nested() { + public NestedPredicateFieldStep nested() { return myPredicateFactory.nested(); } @Override - public NestedPredicateClausesStep nested(String theObjectFieldPath) { + public NestedPredicateClausesStep nested(String theObjectFieldPath) { return myPredicateFactory.nested(theObjectFieldPath); } @Override - public SimpleQueryStringPredicateFieldStep simpleQueryString() { + public SimpleQueryStringPredicateFieldStep simpleQueryString() { return myPredicateFactory.simpleQueryString(); } @Override - public QueryStringPredicateFieldStep queryString() { + public QueryStringPredicateFieldStep queryString() { return myPredicateFactory.queryString(); } @Override - public ExistsPredicateFieldStep exists() { + public ExistsPredicateFieldStep exists() { return myPredicateFactory.exists(); } @@ -308,8 +293,6 @@ public String toAbsolutePath(String relativeFieldPath) { return myPredicateFactory.toAbsolutePath(relativeFieldPath); } - // HSearch uses a dotted path - // Some private static helpers that can be inlined. @Nonnull public static String joinPath(String thePath0, String thePath1) { return thePath0 + PATH_JOINER + thePath1; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java index 4bbed1339e51..a1865a9fa880 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java @@ -42,6 +42,7 @@ import jakarta.persistence.PrePersist; import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -54,7 +55,6 @@ import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; import org.hibernate.type.SqlTypes; -import org.hibernate.validator.constraints.NotBlank; import java.io.Serializable; import java.nio.charset.StandardCharsets; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaCapabilityStatementProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaCapabilityStatementProvider.java index 2ca9e85d393d..01e31d053583 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaCapabilityStatementProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaCapabilityStatementProvider.java @@ -153,8 +153,9 @@ public void setSystemDao(IFhirSystemDao mySystemDao) { protected boolean searchParamEnabled(String theResourceName, String theSearchParam) { return switch (theSearchParam) { case PARAM_FILTER -> myStorageSettings.isFilterParameterEnabled(); - case PARAM_CONTENT, PARAM_TEXT, PARAM_LANGUAGE -> mySearchParamRegistry.hasActiveSearchParam( - theResourceName, theSearchParam, ISearchParamRegistry.SearchParamLookupContextEnum.SEARCH); + case PARAM_CONTENT, PARAM_TEXT, PARAM_LANGUAGE -> + mySearchParamRegistry.hasActiveSearchParam( + theResourceName, theSearchParam, ISearchParamRegistry.SearchParamLookupContextEnum.SEARCH); default -> true; }; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteSearch.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteSearch.java index 87762048c37b..af75cdddc597 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteSearch.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteSearch.java @@ -1,22 +1,3 @@ -/*- - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2025 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ package ca.uhn.fhir.jpa.search.autocomplete; import ca.uhn.fhir.context.FhirContext; @@ -29,11 +10,7 @@ import org.hibernate.search.engine.search.aggregation.AggregationKey; import org.hibernate.search.engine.search.aggregation.SearchAggregation; import org.hibernate.search.engine.search.query.SearchResult; -import org.hibernate.search.engine.search.query.dsl.SearchQueryOptionsStep; -import org.hibernate.search.mapper.orm.search.loading.dsl.SearchLoadingOptionsStep; import org.hibernate.search.mapper.orm.session.SearchSession; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.List; @@ -43,7 +20,6 @@ * Use aggregations to implement a search of most-frequent token search params values. */ class TokenAutocompleteSearch { - private static final Logger ourLog = LoggerFactory.getLogger(TokenAutocompleteSearch.class); private static final AggregationKey AGGREGATION_KEY = AggregationKey.of("autocomplete"); private final FhirContext myFhirContext; @@ -58,10 +34,10 @@ public TokenAutocompleteSearch( } /** - * Search for tokens indexed by theSPName on theResourceName matching theSearchText. + * Search for tokens indexed by theSPName on theResourceName matching theSearchText. * @param theResourceName The resource type (e.g. Observation) * @param theSPName The search param code (e.g. combo-code) - * @param theSearchText The search test (e.g. "bloo") + * @param theSearchText The search text (e.g. "bloo") * @return A collection of Coding elements */ @Nonnull @@ -71,43 +47,37 @@ public List search( TokenAutocompleteAggregation tokenAutocompleteAggregation = new TokenAutocompleteAggregation(theSPName, theCount, theSearchText, theSearchModifier); - // compose the query json - SearchQueryOptionsStep query = mySession + // Run the query with 0 hits; we only need aggregations. + SearchResult result = mySession .search(ResourceTable.class) .where(predFactory -> predFactory.bool(boolBuilder -> { ExtendedHSearchClauseBuilder clauseBuilder = new ExtendedHSearchClauseBuilder( myFhirContext, myStorageSettings, boolBuilder, predFactory); - // we apply resource-level predicates here, at the top level + // apply resource-level predicates if (isNotBlank(theResourceName)) { clauseBuilder.addResourceTypeClause(theResourceName); } })) - .aggregation(AGGREGATION_KEY, buildAggregation(tokenAutocompleteAggregation)); - - // run the query, but with 0 results. We only care about the aggregations. - SearchResult result = query.fetch(0); + .aggregation(AGGREGATION_KEY, buildAggregation(tokenAutocompleteAggregation)) + .fetch(0); // extract the top-n results from the aggregation json. JsonObject resultAgg = result.aggregation(AGGREGATION_KEY); - List aggEntries = tokenAutocompleteAggregation.extractResults(resultAgg); - - return aggEntries; + return tokenAutocompleteAggregation.extractResults(resultAgg); } /** - * Hibernate-search doesn't support nested aggregations, so we use an extension to build what we need from raw JSON. + * Hibernate Search doesn't support nested aggregations, so we use an extension to build from raw JSON. */ SearchAggregation buildAggregation(TokenAutocompleteAggregation tokenAutocompleteAggregation) { JsonObject jsonAggregation = tokenAutocompleteAggregation.toJsonAggregation(); - SearchAggregation aggregation = mySession + return mySession .scope(ResourceTable.class) .aggregation() .extension(ElasticsearchExtension.get()) .fromJson(jsonAggregation) .toAggregation(); - - return aggregation; } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java index 19ba187c114a..13fb452e1676 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java @@ -403,13 +403,13 @@ public void addSortOnResourceLink( mySqlBuilder.addSortDate(datePredicateBuilder.getColumnValueLow(), theAscending, myUseAggregate); return; - /* - * Note that many of the options below aren't implemented because they - * don't seem useful to me, but they could theoretically be implemented - * if someone ever needed them. I'm not sure why you'd want to do a chained - * sort on a target that was a reference or a quantity, but if someone needed - * that we could implement it here. - */ + /* + * Note that many of the options below aren't implemented because they + * don't seem useful to me, but they could theoretically be implemented + * if someone ever needed them. I'm not sure why you'd want to do a chained + * sort on a target that was a reference or a quantity, but if someone needed + * that we could implement it here. + */ case SPECIAL: { if (LOCATION_POSITION.equals(targetSearchParameter.getPath())) { List> params = theParams.get(theParamName); @@ -434,7 +434,7 @@ public void addSortOnResourceLink( return; } } - //noinspection fallthrough + //noinspection fallthrough case NUMBER: case REFERENCE: case COMPOSITE: diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java index 37b10c970bae..37bb686dfb77 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java @@ -938,8 +938,8 @@ public BinaryCondition createConditionForValueWithComparator( case NOT_EQUAL: return BinaryCondition.notEqualTo(theColumn, generatePlaceholder(theValue)); case EQUAL: - // NB: fhir searches are always range searches; - // which is why we do not use "EQUAL" + // NB: fhir searches are always range searches; + // which is why we do not use "EQUAL" case STARTS_AFTER: case APPROXIMATE: case ENDS_BEFORE: diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/elastic/IndexNamePrefixLayoutStrategy.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/elastic/IndexNamePrefixLayoutStrategy.java index 7ba86f99a16c..4864f1a2a71e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/elastic/IndexNamePrefixLayoutStrategy.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/elastic/IndexNamePrefixLayoutStrategy.java @@ -24,7 +24,7 @@ import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import org.apache.commons.lang3.StringUtils; import org.hibernate.search.backend.elasticsearch.index.layout.IndexLayoutStrategy; -import org.hibernate.search.backend.elasticsearch.logging.impl.Log; +import org.hibernate.search.backend.elasticsearch.logging.impl.ElasticsearchLog; import org.hibernate.search.util.common.logging.impl.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -44,7 +44,7 @@ public class IndexNamePrefixLayoutStrategy implements IndexLayoutStrategy { @Autowired private JpaStorageSettings myStorageSettings; - static final Log log = LoggerFactory.make(Log.class, MethodHandles.lookup()); + static final ElasticsearchLog log = LoggerFactory.make(ElasticsearchLog.class, MethodHandles.lookup()); public static final String NAME = "prefix"; public static final Pattern UNIQUE_KEY_EXTRACTION_PATTERN = Pattern.compile("(.*)-\\d{6}"); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java index 3b3ca09ed687..85a918e68109 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java @@ -1395,7 +1395,7 @@ private void addOrRemoveCode( private void handleFilter( String theCodeSystemIdentifier, SearchPredicateFactory theF, - BooleanPredicateClausesStep theB, + BooleanPredicateClausesStep theB, ValueSet.ConceptSetFilterComponent theFilter) { if (isBlank(theFilter.getValue()) && theFilter.getOp() == null && isBlank(theFilter.getProperty())) { return; @@ -1447,7 +1447,7 @@ private void handleFilter( private void handleFilterPropertyDefault( SearchPredicateFactory theF, - BooleanPredicateClausesStep theB, + BooleanPredicateClausesStep theB, ValueSet.ConceptSetFilterComponent theFilter) { String value = theFilter.getValue(); @@ -1497,7 +1497,7 @@ private void handleFilterPropertyDefault( private void handleFilterRegex( SearchPredicateFactory theF, - BooleanPredicateClausesStep theB, + BooleanPredicateClausesStep theB, ValueSet.ConceptSetFilterComponent theFilter) { /* * We treat the regex filter as a match on the regex @@ -1524,7 +1524,7 @@ private void handleFilterRegex( private void handleFilterLoincCopyright( SearchPredicateFactory theF, - BooleanPredicateClausesStep theB, + BooleanPredicateClausesStep theB, ValueSet.ConceptSetFilterComponent theFilter) { if (theFilter.getOp() == ValueSet.FilterOperator.EQUAL) { @@ -1548,11 +1548,11 @@ private void handleFilterLoincCopyright( } } - private void addFilterLoincCopyrightLoinc(SearchPredicateFactory theF, BooleanPredicateClausesStep theB) { + private void addFilterLoincCopyrightLoinc(SearchPredicateFactory theF, BooleanPredicateClausesStep theB) { theB.mustNot(theF.exists().field(CONCEPT_PROPERTY_PREFIX_NAME + "EXTERNAL_COPYRIGHT_NOTICE")); } - private void addFilterLoincCopyright3rdParty(SearchPredicateFactory theF, BooleanPredicateClausesStep theB) { + private void addFilterLoincCopyright3rdParty(SearchPredicateFactory theF, BooleanPredicateClausesStep theB) { theB.must(theF.exists().field(CONCEPT_PROPERTY_PREFIX_NAME + "EXTERNAL_COPYRIGHT_NOTICE")); } @@ -1560,7 +1560,7 @@ private void addFilterLoincCopyright3rdParty(SearchPredicateFactory theF, Boolea private void handleFilterLoincAncestor( String theSystem, SearchPredicateFactory f, - BooleanPredicateClausesStep b, + BooleanPredicateClausesStep b, ValueSet.ConceptSetFilterComponent theFilter) { switch (theFilter.getOp()) { case EQUAL: @@ -1578,7 +1578,7 @@ private void handleFilterLoincAncestor( private void addLoincFilterAncestorEqual( String theSystem, SearchPredicateFactory f, - BooleanPredicateClausesStep b, + BooleanPredicateClausesStep b, ValueSet.ConceptSetFilterComponent theFilter) { addLoincFilterAncestorEqual(theSystem, f, b, theFilter.getProperty(), theFilter.getValue()); } @@ -1586,7 +1586,7 @@ private void addLoincFilterAncestorEqual( private void addLoincFilterAncestorEqual( String theSystem, SearchPredicateFactory f, - BooleanPredicateClausesStep b, + BooleanPredicateClausesStep b, String theProperty, String theValue) { List terms = getAncestorTerms(theSystem, theProperty, theValue); @@ -1597,7 +1597,7 @@ private void addLoincFilterAncestorEqual( private void addLoincFilterAncestorIn( String theSystem, SearchPredicateFactory f, - BooleanPredicateClausesStep b, + BooleanPredicateClausesStep b, ValueSet.ConceptSetFilterComponent theFilter) { String[] values = theFilter.getValue().split(","); List terms = new ArrayList<>(); @@ -1610,7 +1610,9 @@ private void addLoincFilterAncestorIn( @SuppressWarnings("EnumSwitchStatementWhichMissesCases") private void handleFilterLoincParentChild( - SearchPredicateFactory f, BooleanPredicateClausesStep b, ValueSet.ConceptSetFilterComponent theFilter) { + SearchPredicateFactory f, + BooleanPredicateClausesStep b, + ValueSet.ConceptSetFilterComponent theFilter) { switch (theFilter.getOp()) { case EQUAL: addLoincFilterParentChildEqual(f, b, theFilter.getProperty(), theFilter.getValue()); @@ -1625,7 +1627,9 @@ private void handleFilterLoincParentChild( } private void addLoincFilterParentChildIn( - SearchPredicateFactory f, BooleanPredicateClausesStep b, ValueSet.ConceptSetFilterComponent theFilter) { + SearchPredicateFactory f, + BooleanPredicateClausesStep b, + ValueSet.ConceptSetFilterComponent theFilter) { String[] values = theFilter.getValue().split(","); List terms = new ArrayList<>(); for (String value : values) { @@ -1638,7 +1642,7 @@ private void addLoincFilterParentChildIn( } private void addLoincFilterParentChildEqual( - SearchPredicateFactory f, BooleanPredicateClausesStep b, String theProperty, String theValue) { + SearchPredicateFactory f, BooleanPredicateClausesStep b, String theProperty, String theValue) { logFilteringValueOnProperty(theValue, theProperty); b.must(f.match().field(CONCEPT_PROPERTY_PREFIX_NAME + theProperty).matching(theValue)); } @@ -1646,7 +1650,7 @@ private void addLoincFilterParentChildEqual( private void handleFilterConceptAndCode( String theSystem, SearchPredicateFactory f, - BooleanPredicateClausesStep b, + BooleanPredicateClausesStep b, ValueSet.ConceptSetFilterComponent theFilter) { TermConcept code = findCodeForFilterCriteriaCodeOrConcept(theSystem, theFilter); @@ -1697,7 +1701,9 @@ private boolean isCodeSystemLoinc(String theSystem) { } private void handleFilterDisplay( - SearchPredicateFactory f, BooleanPredicateClausesStep b, ValueSet.ConceptSetFilterComponent theFilter) { + SearchPredicateFactory f, + BooleanPredicateClausesStep b, + ValueSet.ConceptSetFilterComponent theFilter) { if (theFilter.getProperty().equals("display:exact") && theFilter.getOp() == ValueSet.FilterOperator.EQUAL) { addDisplayFilterExact(f, b, theFilter); } else if (theFilter.getProperty().equals("display") && theFilter.getOp() == ValueSet.FilterOperator.EQUAL) { @@ -1711,14 +1717,14 @@ private void handleFilterDisplay( private void addDisplayFilterExact( SearchPredicateFactory f, - BooleanPredicateClausesStep bool, + BooleanPredicateClausesStep bool, ValueSet.ConceptSetFilterComponent nextFilter) { bool.must(f.phrase().field("myDisplay").matching(nextFilter.getValue())); } private void addDisplayFilterInexact( SearchPredicateFactory f, - BooleanPredicateClausesStep bool, + BooleanPredicateClausesStep bool, ValueSet.ConceptSetFilterComponent nextFilter) { bool.must(f.phrase() .field("myDisplay") @@ -1752,7 +1758,7 @@ private List getAncestorTerms(String theSystem, String theProperty, String private void handleFilterLoincDescendant( String theSystem, SearchPredicateFactory f, - BooleanPredicateClausesStep b, + BooleanPredicateClausesStep b, ValueSet.ConceptSetFilterComponent theFilter) { switch (theFilter.getOp()) { case EQUAL: @@ -1770,7 +1776,7 @@ private void handleFilterLoincDescendant( private void addLoincFilterDescendantEqual( String theSystem, SearchPredicateFactory f, - BooleanPredicateClausesStep b, + BooleanPredicateClausesStep b, ValueSet.ConceptSetFilterComponent theFilter) { List parentPids = @@ -1797,7 +1803,7 @@ private void addLoincFilterDescendantEqual( private void addLoincFilterDescendantIn( String theSystem, SearchPredicateFactory f, - BooleanPredicateClausesStep b, + BooleanPredicateClausesStep b, ValueSet.ConceptSetFilterComponent theFilter) { String[] values = theFilter.getValue().split(","); @@ -3384,8 +3390,8 @@ public static void setForceDisableHibernateSearchForUnitTest(boolean theForceDis static boolean isPlaceholder(DomainResource theResource) { boolean retVal = false; Extension extension = theResource.getExtensionByUrl(HapiExtensions.EXT_RESOURCE_PLACEHOLDER); - if (extension != null && extension.hasValue() && extension.getValue() instanceof BooleanType) { - retVal = ((BooleanType) extension.getValue()).booleanValue(); + if (extension != null && extension.hasValue() && extension.getValue() instanceof BooleanType booleanType) { + retVal = booleanType.getValue(); } return retVal; } diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/HSearchSandboxTest.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/HSearchSandboxTest.java index 4ce06795bd39..7a76dbcad571 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/HSearchSandboxTest.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/HSearchSandboxTest.java @@ -400,12 +400,12 @@ public PredicateFinalStep buildAndOrPredicates( // we need to know if there is more than one "and" predicate (outer list) with more than one "or" predicate (inner list) long maxOrPredicateSize = theAndOrTerms.stream().map(List::size).filter(s -> s > 1).count(); - BooleanPredicateClausesStep topBool = myPredicateFactory.bool(); + BooleanPredicateClausesStep topBool = myPredicateFactory.bool(); topBool.must(myPredicateFactory.match().field("myResourceType").matching("Observation")); - BooleanPredicateClausesStep activeBool = topBool; + BooleanPredicateClausesStep activeBool = topBool; if (isNested) { - BooleanPredicateClausesStep nestedBool = myPredicateFactory.bool(); + BooleanPredicateClausesStep nestedBool = myPredicateFactory.bool(); activeBool = nestedBool; } @@ -454,7 +454,7 @@ private boolean isNested(String theSearchParamName) { } - private void addOnePredicate(BooleanPredicateClausesStep theTopBool, boolean theIsMust, + private void addOnePredicate(BooleanPredicateClausesStep theTopBool, boolean theIsMust, String theParamName, IQueryParameterType theParameterType) { if (theParameterType instanceof QuantityParam) { @@ -466,7 +466,7 @@ private void addOnePredicate(BooleanPredicateClausesStep theTopBool, boolean } - private void addQuantityOrClauses(BooleanPredicateClausesStep theTopBool, boolean theIsMust, + private void addQuantityOrClauses(BooleanPredicateClausesStep theTopBool, boolean theIsMust, String theSearchParamName, IQueryParameterType theParamType) { String fieldPath = NESTED_SEARCH_PARAM_ROOT + "." + theSearchParamName + "." + INDEX_TYPE_QUANTITY; @@ -479,7 +479,7 @@ private void addQuantityOrClauses(BooleanPredicateClausesStep theTopBool, boo } - private void addQuantityTerms(BooleanPredicateClausesStep theTopBool, boolean theIsMust, + private void addQuantityTerms(BooleanPredicateClausesStep theTopBool, boolean theIsMust, ParamPrefixEnum theActivePrefix, QuantityParam theQtyParam, String theFieldPath) { String valueFieldPath = theFieldPath + "." + QTY_VALUE; @@ -496,12 +496,12 @@ private void addQuantityTerms(BooleanPredicateClausesStep theTopBool, boolean } - private void addFieldPredicate(boolean theIsMust, BooleanPredicateClausesStep theTopBool, String theFieldPath, String theValue) { + private void addFieldPredicate(boolean theIsMust, BooleanPredicateClausesStep theTopBool, String theFieldPath, String theValue) { MatchPredicateOptionsStep pred = myPredicateFactory.match().field(theFieldPath).matching(theValue); addMustOrShould(theIsMust, theTopBool, pred); } - private void addMustOrShould(boolean theIsMust, BooleanPredicateClausesStep theTopBool, PredicateFinalStep thePredicate) { + private void addMustOrShould(boolean theIsMust, BooleanPredicateClausesStep theTopBool, PredicateFinalStep thePredicate) { if (theIsMust) { theTopBool.must(thePredicate); } else { diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatementParser.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatementParser.java index 85673728b202..80f1efd2c784 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatementParser.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatementParser.java @@ -460,10 +460,10 @@ private abstract class BaseRootState extends BaseState { void consume(HfqlLexerToken theToken) { String keyword = theToken.asKeyword(); switch (keyword) { - /* - * Update DIRECTIVE_KEYWORDS if you add new - * keywords here! - */ + /* + * Update DIRECTIVE_KEYWORDS if you add new + * keywords here! + */ case KEYWORD_WHERE: validateNotPresent(myStatement.getWhereClauses(), theToken); myState = new StateInWhereInitial(); diff --git a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/jdbc/HfqlRestClientTest.java b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/jdbc/HfqlRestClientTest.java index bd3e880e52f5..a28a91845c4e 100644 --- a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/jdbc/HfqlRestClientTest.java +++ b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/jdbc/HfqlRestClientTest.java @@ -30,11 +30,11 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.util.Base64Utils; import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Base64; import java.util.List; import java.util.Map; @@ -142,7 +142,7 @@ public void testExecuteSearchAndContinuation() throws SQLException { verify(myFqlExecutor, times(1)).executeInitialSearch(myStatementCaptor.capture(), myLimitCaptor.capture(), myRequestDetailsCaptor.capture()); assertEquals(sql, myStatementCaptor.getValue()); - String expectedAuthHeader = Constants.HEADER_AUTHORIZATION_VALPREFIX_BASIC + Base64Utils.encodeToString((USERNAME + ":" + PASSWORD).getBytes(StandardCharsets.UTF_8)); + String expectedAuthHeader = Constants.HEADER_AUTHORIZATION_VALPREFIX_BASIC + Base64.getEncoder().encodeToString((USERNAME + ":" + PASSWORD).getBytes(StandardCharsets.UTF_8)); String actual = ourHeaderCaptureInterceptor.getCapturedHeaders().get(0).get(Constants.HEADER_AUTHORIZATION).get(0); diff --git a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/jdbc/JdbcDriverTest.java b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/jdbc/JdbcDriverTest.java index 85bbb1b5a8f3..9bc8da2005c9 100644 --- a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/jdbc/JdbcDriverTest.java +++ b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/jdbc/JdbcDriverTest.java @@ -25,7 +25,6 @@ import org.springframework.jdbc.UncategorizedSQLException; import org.springframework.jdbc.core.ColumnMapRowMapper; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.util.Base64Utils; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; @@ -36,6 +35,7 @@ import java.sql.Statement; import java.sql.Timestamp; import java.sql.Types; +import java.util.Base64; import java.util.List; import java.util.Map; @@ -115,7 +115,7 @@ public void testExecuteStatement() { List> outcome = jdbcTemplate.query(input, new ColumnMapRowMapper()); assertThat(outcome).hasSize(2); - String expectedAuthHeader = Constants.HEADER_AUTHORIZATION_VALPREFIX_BASIC + Base64Utils.encodeToString((SOME_USERNAME + ":" + SOME_PASSWORD).getBytes(StandardCharsets.UTF_8)); + String expectedAuthHeader = Constants.HEADER_AUTHORIZATION_VALPREFIX_BASIC + Base64.getEncoder().encodeToString((SOME_USERNAME + ":" + SOME_PASSWORD).getBytes(StandardCharsets.UTF_8)); String actual = ourHeaderCaptureInterceptor.getCapturedHeaders().get(0).get(Constants.HEADER_AUTHORIZATION).get(0); assertEquals(expectedAuthHeader, actual); } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiSequenceStyleGenerator.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiSequenceStyleGenerator.java index 1a90a1c43c0f..e1b19e2bfd01 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiSequenceStyleGenerator.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiSequenceStyleGenerator.java @@ -1,35 +1,15 @@ -/*- - * #%L - * HAPI FHIR JPA Model - * %% - * Copyright (C) 2014 - 2025 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ package ca.uhn.fhir.jpa.model.dialect; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.util.ISequenceValueMassager; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.apache.commons.lang3.Validate; import org.hibernate.HibernateException; -import org.hibernate.MappingException; import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.model.relational.ExportableProducer; import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.generator.GeneratorCreationContext; import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.OptimizableGenerator; @@ -37,9 +17,6 @@ import org.hibernate.id.enhanced.Optimizer; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.id.enhanced.StandardOptimizerDescriptor; -import org.hibernate.service.ServiceRegistry; -import org.hibernate.type.Type; -import org.springframework.beans.factory.annotation.Autowired; import java.io.Serializable; import java.util.Properties; @@ -47,17 +24,16 @@ import static ca.uhn.fhir.jpa.model.util.JpaConstants.NO_MORE_PID; /** - * This is a sequence generator that wraps the Hibernate default sequence generator {@link SequenceStyleGenerator} - * and by default will therefore work exactly as the default would, but allows for customization. + * Hibernate 7.1.1 compatible sequence generator that wraps SequenceStyleGenerator and + * optionally massages values via ISequenceValueMassager. */ @SuppressWarnings("unused") public class HapiSequenceStyleGenerator implements PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator, ExportableProducer { + public static final String ID_MASSAGER_TYPE_KEY = "hapi_fhir.sequence_generator_massager"; - private final SequenceStyleGenerator myGen = new SequenceStyleGenerator(); - @Autowired - private StorageSettings myStorageSettings; + private final SequenceStyleGenerator myGen = new SequenceStyleGenerator(); private ISequenceValueMassager myIdMassager; private boolean myConfigured; @@ -76,17 +52,12 @@ public String determineBulkInsertionIdentifierGenerationSelectFragment(SqlString @Override public Serializable generate(SharedSessionContractImplementor theSession, Object theObject) throws HibernateException { + Long retVal = myIdMassager != null ? myIdMassager.generate(myGeneratorName) : null; if (retVal == null) { Long next = (Long) myGen.generate(theSession, theObject); - retVal = myIdMassager.massage(myGeneratorName, next); - /* - * This should never happen since the sequence starts at 1, but if someone ever manually messes with sequences - * or the sequence otherwise gets messed up, we don't want to end up with a resource using this PID which has - * a special meaning to HAPI. - */ if (NO_MORE_PID.equals(next) || NO_MORE_PID.equals(retVal)) { throw new InternalErrorException( Msg.code(2791) + "Resource ID generator provided illegal value: " + next + " / " + retVal); @@ -96,26 +67,21 @@ public Serializable generate(SharedSessionContractImplementor theSession, Object } @Override - public void configure(Type theType, Properties theParams, ServiceRegistry theServiceRegistry) - throws MappingException { - - myIdMassager = theServiceRegistry.getService(ISequenceValueMassager.class); + public void configure(GeneratorCreationContext creationContext, Properties parameters) { + myIdMassager = creationContext.getServiceRegistry().getService(ISequenceValueMassager.class); if (myIdMassager == null) { myIdMassager = new ISequenceValueMassager.NoopSequenceValueMassager(); } - - // Create a HAPI FHIR sequence style generator - myGeneratorName = theParams.getProperty(IdentifierGenerator.GENERATOR_NAME); + myGeneratorName = parameters.getProperty(IdentifierGenerator.GENERATOR_NAME); Validate.notBlank(myGeneratorName, "No generator name found"); - Properties props = new Properties(theParams); + Properties props = new Properties(parameters); props.put(OptimizableGenerator.OPT_PARAM, StandardOptimizerDescriptor.POOLED.getExternalName()); props.put(OptimizableGenerator.INITIAL_PARAM, "1"); props.put(OptimizableGenerator.INCREMENT_PARAM, "50"); - props.put(GENERATOR_NAME, myGeneratorName); - - myGen.configure(theType, props, theServiceRegistry); + props.put(IdentifierGenerator.GENERATOR_NAME, myGeneratorName); + myGen.configure(creationContext, props); myConfigured = true; } @@ -129,11 +95,6 @@ public void initialize(SqlStringGenerationContext context) { myGen.initialize(context); } - @Override - public boolean supportsJdbcBatchInserts() { - return myGen.supportsJdbcBatchInserts(); - } - @Override public Optimizer getOptimizer() { return myGen.getOptimizer(); diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java index 4259aaa99e7e..0af2efb1b6e4 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java @@ -55,9 +55,6 @@ import jakarta.persistence.Version; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import org.hibernate.Session; -import org.hibernate.annotations.GenerationTime; -import org.hibernate.annotations.GeneratorType; import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.annotations.OptimisticLock; import org.hibernate.search.engine.backend.types.Projectable; @@ -74,7 +71,6 @@ import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ObjectPath; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.PropertyBinding; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.PropertyValue; -import org.hibernate.tuple.ValueGenerator; import org.hibernate.type.SqlTypes; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.InstantType; @@ -381,9 +377,6 @@ public class ResourceTable extends BaseHasResource implements Serializab length = FHIR_ID_LENGTH, // we never update this after insert, and the Generator will otherwise "dirty" the object. updatable = false) - - // inject the pk for server-assigned sequence ids. - @GeneratorType(when = GenerationTime.INSERT, type = FhirIdGenerator.class) // Make sure the generator doesn't bump the history version. @OptimisticLock(excluded = true) private String myFhirId; @@ -970,6 +963,15 @@ public String toString() { @PrePersist @PreUpdate public void preSave() { + /** + * Pre-insert hook used to populate `myFhirId` with the server-assigned sequence id + * when no client-assigned id is provided. This emulates the old Hibernate insert + * generator behavior. + */ + if ((myFhirId == null || myFhirId.isEmpty()) && myPid != null && myPid.getId() != null) { + myFhirId = myPid.getId().toString(); + } + if (myHasLinks && myResourceLinks != null) { myResourceLinksField = getResourceLinks().stream() .map(ResourceLink::getTargetResourcePid) @@ -1107,18 +1109,4 @@ public void setParamsComboStringUnique(Collection theComboTokensNonUnique) { myParamsComboTokensNonUnique = theComboTokensNonUnique; } - - /** - * Populate myFhirId with server-assigned sequence id when no client-id provided. - * We eat this complexity during insert to simplify query time with a uniform column. - * Server-assigned sequence ids aren't available until just before insertion. - * Hibernate calls insert Generators after the pk has been assigned, so we can use myId safely here. - */ - public static final class FhirIdGenerator implements ValueGenerator { - @Override - public String generateValue(Session session, Object owner) { - ResourceTable that = (ResourceTable) owner; - return that.myFhirId != null ? that.myFhirId : that.myPid.getId().toString(); - } - } } diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java index c27533888955..6ef1c34e5ff8 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java @@ -100,7 +100,7 @@ public RuntimeSearchParam canonicalizeSearchParameter(IBaseResource theSearchPar break; case DSTU2_HL7ORG: case DSTU2_1: - // Non-supported - these won't happen so just fall through + // Non-supported - these won't happen so just fall through default: throw new InternalErrorException( Msg.code(510) + "SearchParameter canonicalization not supported for FHIR version" diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/RuntimeSearchParamHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/RuntimeSearchParamHelper.java index 71523bf6db1f..57efbbf3ffa5 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/RuntimeSearchParamHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/RuntimeSearchParamHelper.java @@ -67,8 +67,8 @@ public static boolean isSpeciallyHandledSearchParameter( case PARAM_LANGUAGE -> false; case PARAM_SOURCE -> true; case PARAM_TEXT -> true; - case PARAM_PROFILE, PARAM_TAG, PARAM_SECURITY -> theStorageSettings.getTagStorageMode() - != StorageSettings.TagStorageModeEnum.INLINE; + case PARAM_PROFILE, PARAM_TAG, PARAM_SECURITY -> + theStorageSettings.getTagStorageMode() != StorageSettings.TagStorageModeEnum.INLINE; default -> false; }; } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilder.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilder.java index 799cfe12c9b8..7f5412edd886 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilder.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilder.java @@ -241,7 +241,7 @@ private void addResources( BundleBuilder theBundleBuilder) { org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent content = - ObjectUtils.defaultIfNull(theCanonicalSubscription.getContent(), FULLRESOURCE); + ObjectUtils.getIfNull(theCanonicalSubscription.getContent(), FULLRESOURCE); switch (content) { case IDONLY: diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java index 6026e962ca90..06a95a2756b6 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java @@ -6,6 +6,7 @@ import static ca.uhn.fhir.interceptor.model.RequestPartitionId.fromPartitionNames; import static ca.uhn.fhir.jpa.model.entity.ResourceTable.IDX_RES_TYPE_FHIR_ID; import static org.junit.jupiter.api.Assertions.assertNotNull; + import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; @@ -74,7 +75,7 @@ public void after() { myPartitionSettings.setAllowReferencesAcrossPartitions(defaultPartitionSettings.getAllowReferencesAcrossPartitions()); myPartitionSettings.setDefaultPartitionId(defaultPartitionSettings.getDefaultPartitionId()); - mySrdInterceptorService.unregisterInterceptorsIf(t -> t instanceof MyReadWriteInterceptor); + mySrdInterceptorService.unregisterInterceptorsIf(MyReadWriteInterceptor.class::isInstance); myStorageSettings.setIndexMissingFields(defaultStorageSettings.getIndexMissingFields()); myStorageSettings.setAutoCreatePlaceholderReferenceTargets(defaultStorageSettings.isAutoCreatePlaceholderReferenceTargets()); @@ -84,6 +85,8 @@ public void after() { if (myRegisteredSearchParamValidatingInterceptor) { myInterceptorRegistry.unregisterInterceptor(mySearchParamValidatingInterceptor); } + + afterPurgeDatabase(); } protected void assertNoRemainingPartitionIds() { @@ -128,7 +131,7 @@ public void before() throws Exception { myPartitionConfigSvc.getPartitionById(i); } - if (myInterceptorRegistry.getAllRegisteredInterceptors().stream().noneMatch(t->t instanceof SearchParamValidatingInterceptor)) { + if (myInterceptorRegistry.getAllRegisteredInterceptors().stream().noneMatch(t -> t instanceof SearchParamValidatingInterceptor)) { myRegisteredSearchParamValidatingInterceptor = true; myInterceptorRegistry.registerInterceptor(mySearchParamValidatingInterceptor); } @@ -287,8 +290,10 @@ protected void addNextTargetPartitionForCreateWithId(RequestPartitionId requestP addNextInterceptorCreateResult(requestPartitionId); } - /** Actual update of an existing resource. - * We only need one call for the tx boundary, since the actual partition is already assigned. */ + /** + * Actual update of an existing resource. + * We only need one call for the tx boundary, since the actual partition is already assigned. + */ protected void addNextTargetPartitionForUpdate(RequestPartitionId theRequestPartitionId) { addNextInterceptorCreateResult(theRequestPartitionId); } @@ -430,7 +435,7 @@ public void addNextIterceptorReadResult(RequestPartitionId theRequestPartitionId @Hook(Pointcut.STORAGE_PARTITION_IDENTIFY_READ) public RequestPartitionId partitionIdentifyRead(ServletRequestDetails theRequestDetails, - ReadPartitionIdRequestDetails theDetails) { + ReadPartitionIdRequestDetails theDetails) { // Just to be nice, figure out the first line in the stack that isn't a part of the // partitioning or interceptor infrastructure, just so it's obvious who is asking @@ -464,10 +469,10 @@ private static String getCallerStackLine() { } catch (Exception e) { stack = StackTraceHelper.getStackAsString(e); stack = Arrays.stream(stack.split("\\n")) - .filter(t->t.contains("ca.uhn.fhir")) - .filter(t->!t.toLowerCase().contains("interceptor")) - .filter(t->!t.toLowerCase().contains("partitionhelper")) - .filter(t->!t.contains("Test")) + .filter(t -> t.contains("ca.uhn.fhir")) + .filter(t -> !t.toLowerCase().contains("interceptor")) + .filter(t -> !t.toLowerCase().contains("partitionhelper")) + .filter(t -> !t.contains("Test")) .findFirst() .orElse("UNKNOWN"); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4IndexStorageOptimizedTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4IndexStorageOptimizedTest.java index 8f5c3fdcd43a..f98e414ad872 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4IndexStorageOptimizedTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4IndexStorageOptimizedTest.java @@ -300,7 +300,7 @@ private List getAndValidateIndexedSear JpaRepository theIndexedSpRepository, IIdType theId, String theSearchParam, String theResourceType) { long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), - RequestPartitionId.defaultPartition(), theResourceType, theSearchParam); + RequestPartitionId.allPartitions(), theResourceType, theSearchParam); await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { IndexedSearchParamIdentity spIdentity = runInTransaction(() -> diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitionedStrictTransactionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitionedStrictTransactionR4Test.java index aa9f4f6e4ae5..9a1e7ac8b7f5 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitionedStrictTransactionR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitionedStrictTransactionR4Test.java @@ -47,9 +47,9 @@ public void before() throws Exception { @Override public void after() { - super.after(); myTransactionService.setTransactionPropagationWhenChangingPartitions(HapiTransactionService.DEFAULT_TRANSACTION_PROPAGATION_WHEN_CHANGING_PARTITIONS); - myInterceptorRegistry.unregisterInterceptorsIf(t -> t instanceof MyPartitionSelectorInterceptor); + myInterceptorRegistry.unregisterInterceptorsIf(MyPartitionSelectorInterceptor.class::isInstance); + super.after(); } /** @@ -89,6 +89,7 @@ public void testSinglePartitionCreate(String theBundleType, int theExpectedCommi IdType id = new IdType(output.getEntry().get(0).getResponse().getLocation()); Patient actualPatient = myPatientDao.read(id, mySrd); RequestPartitionId actualPartitionId = (RequestPartitionId) actualPatient.getUserData(Constants.RESOURCE_PARTITION_ID); + assertThat(actualPartitionId).isNotNull(); assertThat(actualPartitionId.getPartitionIds()).containsExactly(myPartitionId); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningAllowedUnqualifiedR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningAllowedUnqualifiedR4Test.java index 0278e611eb60..6bcc75987957 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningAllowedUnqualifiedR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningAllowedUnqualifiedR4Test.java @@ -30,7 +30,7 @@ public void after() { myPartitionSettings.setDefaultPartitionId(defaultPartitionSettings.getDefaultPartitionId()); mySrdInterceptorService.unregisterInterceptorsIf(MyReadWriteInterceptor.class::isInstance); - + afterPurgeDatabase(); } @Test diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvcTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvcTest.java index 0bd5fa1d054e..438ef1a137fb 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvcTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvcTest.java @@ -16,7 +16,6 @@ import ca.uhn.test.util.LogbackTestExtension; import ca.uhn.test.util.LogbackTestExtensionAssert; import jakarta.annotation.Nullable; -import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.HumanName; @@ -42,7 +41,7 @@ public class ThreadSafeResourceDeleterSvcTest extends BaseJpaR4Test { private ThreadSafeResourceDeleterSvc myThreadSafeResourceDeleterSvc; @RegisterExtension - final LogbackTestExtension mySqlExceptionHelperLog = new LogbackTestExtension(SqlExceptionHelper.class); + final LogbackTestExtension mySqlExceptionHelperLog = new LogbackTestExtension("org.hibernate.orm.jdbc.error"); @Autowired IInterceptorBroadcaster myIdInterceptorBroadcaster; @@ -57,7 +56,7 @@ public class ThreadSafeResourceDeleterSvcTest extends BaseJpaR4Test { @BeforeEach - void beforeEach() { + void beforeEach() throws Exception { myThreadSafeResourceDeleterSvc = new ThreadSafeResourceDeleterSvc(myDaoRegistry, myIdInterceptorBroadcaster, myHapiTransactionService); } @@ -176,7 +175,7 @@ void delete_update_retryTest() throws ExecutionException, InterruptedException { // Unpause and succeed in deleting the second patient because we will get the correct version now // Red Green: If you delete the updatePatient above, it will timeout here phaser.arriveAndAwaitSharedEndOf(UpdateSteps.DEL_BEFORE_SECOND_PATIENT_DELETE_SUCCEED); - LogbackTestExtensionAssert.assertThat(mySqlExceptionHelperLog).hasErrorMessage("Unique index or primary key violation"); + LogbackTestExtensionAssert.assertThat(mySqlExceptionHelperLog).hasWarnMessage("Unique index or primary key violation"); phaser.assertInPhase(UpdateSteps.BOTH_COMPLETE); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java index 76448038d731..4f5dd1da47c2 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java @@ -1,7 +1,9 @@ package ca.uhn.fhir.jpa.interceptor; import static ca.uhn.fhir.interceptor.model.RequestPartitionId.defaultPartition; +import static ca.uhn.fhir.interceptor.model.RequestPartitionId.fromPartitionId; import static org.junit.jupiter.api.Assertions.assertNotNull; + import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Interceptor; @@ -14,7 +16,6 @@ import ca.uhn.fhir.jpa.interceptor.ex.PartitionInterceptorReadAllPartitions; import ca.uhn.fhir.jpa.interceptor.ex.PartitionInterceptorReadPartitionsBasedOnScopes; import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; @@ -32,7 +33,6 @@ import org.hl7.fhir.r4.model.StructureDefinition; import org.hl7.fhir.r4.model.Subscription; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockHttpServletRequest; import jakarta.servlet.http.HttpServletRequest; @@ -46,7 +46,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -105,14 +104,14 @@ public void testCrossPartitionUpdate() { assertEquals(2, writeIndex.get()); } finally { myInterceptorRegistry.unregisterInterceptor(myPartitionInterceptor); - myInterceptorRegistry.unregisterInterceptorsIf(t->t instanceof MySubscriptionReadInterceptor); - myInterceptorRegistry.unregisterInterceptorsIf(t->t instanceof MySubscriptionWriteInterceptor); + myInterceptorRegistry.unregisterInterceptorsIf(MySubscriptionReadInterceptor.class::isInstance); + myInterceptorRegistry.unregisterInterceptorsIf(MySubscriptionWriteInterceptor.class::isInstance); } } @Test public void testCreateNonPartionableResourceWithPartitionDate() { - addNextTargetPartitionForCreate(defaultPartition(LocalDate.of(2021, 2, 22))); + addNextTargetPartitionForCreate(fromPartitionId(null, LocalDate.of(2021, 2, 22))); StructureDefinition sd = new StructureDefinition(); sd.setUrl("http://foo"); @@ -129,7 +128,7 @@ public void testCreateNonPartionableResourceWithPartitionDate() { @Test public void testCreateNonPartionableResourceWithNullPartitionReturned() { - addNextTargetPartitionForCreate(RequestPartitionId.defaultPartition()); + addNextTargetPartitionForCreate(RequestPartitionId.allPartitions()); StructureDefinition sd = new StructureDefinition(); sd.setUrl("http://foo"); @@ -177,7 +176,7 @@ public void testSearch_InterceptorForAllPartitions() { IIdType patientId1 = createPatient(withCreatePartition(1), withActiveTrue()); IIdType patientId2 = createPatient(withCreatePartition(2), withActiveTrue()); - + ourLog.info("Created patients: {}, {}, {}", patientIdNull, patientId1, patientId2); PartitionInterceptorReadAllPartitions interceptor = new PartitionInterceptorReadAllPartitions(); myInterceptorRegistry.registerInterceptor(interceptor); try { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java index 5fdf25884f45..c29522411aa0 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java @@ -55,18 +55,27 @@ import com.google.common.collect.Lists; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; +import jakarta.persistence.CacheRetrieveMode; +import jakarta.persistence.CacheStoreMode; +import jakarta.persistence.ConnectionConsumer; +import jakarta.persistence.ConnectionFunction; import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.EntityTransaction; +import jakarta.persistence.FindOption; import jakarta.persistence.FlushModeType; import jakarta.persistence.LockModeType; +import jakarta.persistence.LockOption; import jakarta.persistence.Query; +import jakarta.persistence.RefreshOption; import jakarta.persistence.StoredProcedureQuery; import jakarta.persistence.TypedQuery; +import jakarta.persistence.TypedQueryReference; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.CriteriaSelect; import jakarta.persistence.criteria.CriteriaUpdate; import jakarta.persistence.metamodel.Metamodel; import org.hibernate.internal.SessionImpl; @@ -655,11 +664,26 @@ public T find(Class entityClass, Object primaryKey, LockModeType lockMode throw new UnsupportedOperationException(); } + @Override + public T find(Class entityClass, Object primaryKey, FindOption... options) { + throw new UnsupportedOperationException(); + } + + @Override + public T find(EntityGraph entityGraph, Object primaryKey, FindOption... options) { + throw new UnsupportedOperationException(); + } + @Override public T getReference(Class entityClass, Object primaryKey) { throw new UnsupportedOperationException(); } + @Override + public T getReference(T entity) { + throw new UnsupportedOperationException(); + } + @Override public void flush() { myFlushCount++; @@ -685,6 +709,11 @@ public void lock(Object entity, LockModeType lockMode, Map prope throw new UnsupportedOperationException(); } + @Override + public void lock(Object entity, LockModeType lockMode, LockOption... options) { + throw new UnsupportedOperationException(); + } + @Override public void refresh(Object entity) { throw new UnsupportedOperationException(); @@ -705,6 +734,11 @@ public void refresh(Object entity, LockModeType lockMode, Map pr throw new UnsupportedOperationException(); } + @Override + public void refresh(Object entity, RefreshOption... options) { + throw new UnsupportedOperationException(); + } + @Override public void clear() { throw new UnsupportedOperationException(); @@ -725,6 +759,26 @@ public LockModeType getLockMode(Object entity) { throw new UnsupportedOperationException(); } + @Override + public void setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode) { + throw new UnsupportedOperationException(); + } + + @Override + public void setCacheStoreMode(CacheStoreMode cacheStoreMode) { + throw new UnsupportedOperationException(); + } + + @Override + public CacheRetrieveMode getCacheRetrieveMode() { + throw new UnsupportedOperationException(); + } + + @Override + public CacheStoreMode getCacheStoreMode() { + throw new UnsupportedOperationException(); + } + @Override public void setProperty(String propertyName, Object value) { throw new UnsupportedOperationException(); @@ -745,6 +799,11 @@ public TypedQuery createQuery(CriteriaQuery criteriaQuery) { throw new UnsupportedOperationException(); } + @Override + public TypedQuery createQuery(CriteriaSelect selectQuery) { + throw new UnsupportedOperationException(); + } + @Override public Query createQuery(CriteriaUpdate updateQuery) { throw new UnsupportedOperationException(); @@ -770,6 +829,11 @@ public TypedQuery createNamedQuery(String name, Class resultClass) { throw new UnsupportedOperationException(); } + @Override + public TypedQuery createQuery(TypedQueryReference reference) { + throw new UnsupportedOperationException(); + } + @Override public Query createNativeQuery(String sqlString) { throw new UnsupportedOperationException(); @@ -878,6 +942,16 @@ public List> getEntityGraphs(Class entityClass) { throw new UnsupportedOperationException(); } + @Override + public void runWithConnection(ConnectionConsumer action) { + throw new UnsupportedOperationException(); + } + + @Override + public T callWithConnection(ConnectionFunction function) { + throw new UnsupportedOperationException(); + } + public void clearCounts() { myMergeCount.clear(); myPersistCount.clear(); @@ -891,18 +965,18 @@ private static class MockRequestPartitionHelperSvc implements ca.uhn.fhir.jpa.pa @Nonnull @Override public RequestPartitionId determineReadPartitionForRequest(@Nullable RequestDetails theRequest, @Nonnull ReadPartitionIdRequestDetails theDetails) { - return RequestPartitionId.defaultPartition(); + return RequestPartitionId.allPartitions(); } @Override public RequestPartitionId determineGenericPartitionForRequest(RequestDetails theRequestDetails) { - return RequestPartitionId.defaultPartition(); + return RequestPartitionId.allPartitions(); } @Nonnull @Override public RequestPartitionId determineCreatePartitionForRequest(@Nullable RequestDetails theRequest, @Nonnull IBaseResource theResource, @Nonnull String theResourceType) { - return RequestPartitionId.defaultPartition(); + return RequestPartitionId.allPartitions(); } @Nonnull @@ -919,15 +993,14 @@ public boolean isResourcePartitionable(String theResourceType) { @Override public RequestPartitionId validateAndNormalizePartitionIds(RequestPartitionId theRequestPartitionId) { - return RequestPartitionId.defaultPartition(); + return RequestPartitionId.allPartitions(); } @Override public RequestPartitionId validateAndNormalizePartitionNames(RequestPartitionId theRequestPartitionId) { - return RequestPartitionId.defaultPartition(); + return RequestPartitionId.allPartitions(); } - } private static class MockTransactionManager implements PlatformTransactionManager { diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index b6c97df79d8e..61aecc27885a 100644 --- a/hapi-fhir-jpaserver-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-test-utilities/pom.xml @@ -145,6 +145,11 @@ ojdbc11 21.5.0.0 + + org.hibernate.orm + hibernate-community-dialects + ${hibernate_version} + org.testcontainers testcontainers diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/term/IValueSetExpansionIT.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/term/IValueSetExpansionIT.java index 65add9a34d2b..bd8baf0d64f8 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/term/IValueSetExpansionIT.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/term/IValueSetExpansionIT.java @@ -180,8 +180,8 @@ default void expandByIdentifier_withFiltersThatShouldNotMatchInInclude_addsNoNew case NOTIN -> filterComponent.setValue("1,2,3"); case EQUAL -> filterComponent.setValue("2"); default -> - // just in case - fail(theOperator.getDisplay() + " is not added for testing"); + // just in case + fail(theOperator.getDisplay() + " is not added for testing"); } conceptSetComponent.setFilter(List.of(filterComponent)); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java index 148c4b4d3174..aa5d34b262bb 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java @@ -489,8 +489,7 @@ public void afterPerformCleanup() { @AfterEach public void afterValidateNoTransaction() { PlatformTransactionManager txManager = getTxManager(); - if (txManager instanceof JpaTransactionManager) { - JpaTransactionManager hibernateTxManager = (JpaTransactionManager) txManager; + if (txManager instanceof JpaTransactionManager hibernateTxManager) { SessionFactory sessionFactory = (SessionFactory) hibernateTxManager.getEntityManagerFactory(); AtomicBoolean isReadOnly = new AtomicBoolean(); Session currentSession; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/ResetSequencesTestHelper.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/ResetSequencesTestHelper.java index 5579d362049f..6234f9e0e56d 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/ResetSequencesTestHelper.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/ResetSequencesTestHelper.java @@ -77,8 +77,7 @@ public void afterEach(ExtensionContext theExtensionContext) throws Exception { */ MappingMetamodelImpl metamodel = (MappingMetamodelImpl)entityManager.getMetamodel(); - - EntityPersister persister = metamodel.entityPersister(ResourceTable.class); + EntityPersister persister = metamodel.findEntityDescriptor(ResourceTable.class); List generationPlans = getFieldValue(persister.getGenerator(), "generationPlans"); Component.ValueGenerationPlan plan = generationPlans.get(0); diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderTest.java index cceb11ae7d4a..901f60ed97c3 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderTest.java @@ -11,10 +11,10 @@ import ca.uhn.fhir.jpa.search.builder.predicate.ResourceTablePredicateBuilder; import ca.uhn.fhir.rest.api.SearchIncludeDeletedEnum; import com.google.common.collect.Lists; -import org.hibernate.dialect.DerbyDialect; -import org.hibernate.dialect.MySQL8Dialect; +import org.hibernate.community.dialect.DerbyDialect; +import org.hibernate.community.dialect.SQLServerLegacyDialect; +import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.PostgreSQLDialect; -import org.hibernate.dialect.SQLServer2012Dialect; import org.hibernate.dialect.SQLServerDialect; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -109,7 +109,7 @@ public void testRangeSqlServer2005_WithSort() { public void testRangeSqlServer2012_NoSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new SQLServer2012Dialect()); + dialectProvider.setDialectForUnitTest(new SQLServerLegacyDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false, false); builder.addResourceIdsPredicate(Lists.newArrayList(JpaPid.fromId(500L), JpaPid.fromId(501L))); GeneratedSql generated; @@ -135,7 +135,7 @@ public void testRangeSqlServer2012_NoSort() { public void testRangeSqlServer2012_WithSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new SQLServer2012Dialect()); + dialectProvider.setDialectForUnitTest(new SQLServerLegacyDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false, false); builder.addResourceIdsPredicate(Lists.newArrayList(JpaPid.fromId(500L), JpaPid.fromId(501L))); builder.addSortDate(builder.getOrCreateResourceTablePredicateBuilder().getColumnLastUpdated(), true); @@ -268,7 +268,7 @@ public void testRangeOracle12c_WithSort() { public void testRangeMySQL8_NoSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new MySQL8Dialect()); + dialectProvider.setDialectForUnitTest(new MySQLDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false, false); builder.addResourceIdsPredicate(Lists.newArrayList(JpaPid.fromId(500L), JpaPid.fromId(501L))); GeneratedSql generated; @@ -294,7 +294,7 @@ public void testRangeMySQL8_NoSort() { public void testRangeMySQL8_WithSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new MySQL8Dialect()); + dialectProvider.setDialectForUnitTest(new MySQLDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false, false); builder.addResourceIdsPredicate(Lists.newArrayList(JpaPid.fromId(500L), JpaPid.fromId(501L))); builder.addSortDate(builder.getOrCreateResourceTablePredicateBuilder().getColumnLastUpdated(), true); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/.gitignore b/hapi-fhir-jpaserver-uhnfhirtest/.gitignore index be49b6ec8ff5..2b54f7948860 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/.gitignore +++ b/hapi-fhir-jpaserver-uhnfhirtest/.gitignore @@ -127,4 +127,3 @@ local.properties # TeXlipse plugin .texlipse - diff --git a/hapi-fhir-repositories/src/main/java/ca/uhn/fhir/repository/impl/NaiveRepositoryTransactionProcessor.java b/hapi-fhir-repositories/src/main/java/ca/uhn/fhir/repository/impl/NaiveRepositoryTransactionProcessor.java index 3c2b7addf480..b8063212a457 100644 --- a/hapi-fhir-repositories/src/main/java/ca/uhn/fhir/repository/impl/NaiveRepositoryTransactionProcessor.java +++ b/hapi-fhir-repositories/src/main/java/ca/uhn/fhir/repository/impl/NaiveRepositoryTransactionProcessor.java @@ -102,8 +102,9 @@ public B processTransaction(B theTransactionBundle) { case POST -> processPost(e, now); case PUT -> processPut(e, now); case DELETE -> processDelete(e, now); - default -> throw new NotImplementedOperationException( - Msg.code(2769) + "Transaction stub only supports POST, PUT, or DELETE"); + default -> + throw new NotImplementedOperationException( + Msg.code(2769) + "Transaction stub only supports POST, PUT, or DELETE"); }; bundleBuilder.addEntry(myResponseEntryBuilder.apply(responseEntry)); } @@ -217,8 +218,9 @@ protected static String statusCodeToStatusLine(int theResponseStatusCode) { case Constants.STATUS_HTTP_409_CONFLICT -> "409 Conflict"; case Constants.STATUS_HTTP_204_NO_CONTENT -> "204 No Content"; case Constants.STATUS_HTTP_404_NOT_FOUND -> "404 Not Found"; - default -> throw new IllegalArgumentException( - Msg.code(2776) + "Unsupported response status code: " + theResponseStatusCode); + default -> + throw new IllegalArgumentException( + Msg.code(2776) + "Unsupported response status code: " + theResponseStatusCode); }; } } diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index a241c24e6f5b..a7df0948c025 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -85,6 +85,11 @@ spring-test test + + org.junit.platform + junit-platform-launcher + test + org.springframework diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BaseResponseTerminologyInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BaseResponseTerminologyInterceptor.java index d21d0c0f2a66..83963142f70a 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BaseResponseTerminologyInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BaseResponseTerminologyInterceptor.java @@ -52,7 +52,7 @@ public BaseResponseTerminologyInterceptor(@Nonnull IValidationSupport theValidat protected List toListForProcessing(RequestDetails theRequestDetails, IBaseResource theResource) { switch (theRequestDetails.getRestOperationType()) { - // Don't apply to these operations + // Don't apply to these operations case ADD_TAGS: case DELETE_TAGS: case GET_TAGS: @@ -74,7 +74,7 @@ protected List toListForProcessing(RequestDetails theRequestDetai default: return Collections.emptyList(); - // Do apply to these operations + // Do apply to these operations case HISTORY_INSTANCE: case HISTORY_SYSTEM: case HISTORY_TYPE: diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java index 6436b7ce12fa..bcf7e9c53688 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java @@ -193,7 +193,7 @@ public Verdict applyRule( theOutputResource, theRuleApplier); - // None of the following are checked on the way in + // None of the following are checked on the way in case ADD_TAGS: case DELETE_TAGS: case GET_TAGS: diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/ValidationMessageUnknownCodeSystemPostProcessingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/ValidationMessageUnknownCodeSystemPostProcessingInterceptor.java index 0f106a8953fa..02ac68ca7605 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/ValidationMessageUnknownCodeSystemPostProcessingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/ValidationMessageUnknownCodeSystemPostProcessingInterceptor.java @@ -79,7 +79,7 @@ private static ResultSeverityEnum mapIssueSeverityToResultSeverityEnum( case FATAL -> ResultSeverityEnum.FATAL; case ERROR -> ResultSeverityEnum.ERROR; case WARNING -> ResultSeverityEnum.WARNING; - // treat success as information level + // treat success as information level case INFORMATION, SUCCESS -> ResultSeverityEnum.INFORMATION; }; } diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index 24b842f9894d..958ed9eead95 100644 --- a/hapi-fhir-sql-migrate/pom.xml +++ b/hapi-fhir-sql-migrate/pom.xml @@ -132,6 +132,11 @@ ${project.version} test + + + org.hibernate.orm + hibernate-community-dialects + diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/DriverTypeEnum.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/DriverTypeEnum.java index 25be0f634b4d..42c88ec62ed5 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/DriverTypeEnum.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/DriverTypeEnum.java @@ -167,9 +167,9 @@ public static class ConnectionProperties implements AutoCloseable { */ public ConnectionProperties( DataSource theDataSource, TransactionTemplate theTxTemplate, DriverTypeEnum theDriverType) { - Validate.notNull(theDataSource); - Validate.notNull(theTxTemplate); - Validate.notNull(theDriverType); + Validate.notNull(theDataSource, "The data source must not be null"); + Validate.notNull(theTxTemplate, "The transaction template must not be null"); + Validate.notNull(theDriverType, "The driver type must not be null"); myDataSource = theDataSource; myTxTemplate = theTxTemplate; @@ -199,9 +199,9 @@ public TransactionTemplate getTxTemplate() { @Override public void close() { - if (myDataSource instanceof DisposableBean) { + if (myDataSource instanceof DisposableBean disposableBean) { try { - ((DisposableBean) myDataSource).destroy(); + disposableBean.destroy(); } catch (Exception e) { ourLog.warn("Could not dispose of driver", e); } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java index 516feeeca1c7..d1418b5f791a 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java @@ -29,6 +29,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.relational.QualifiedSequenceName; +import org.hibernate.community.dialect.CommunityDialectResolver; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver; import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter; @@ -70,9 +71,11 @@ import java.util.stream.StreamSupport; import javax.sql.DataSource; -public class JdbcUtils { +public final class JdbcUtils { private static final Logger ourLog = LoggerFactory.getLogger(JdbcUtils.class); + private JdbcUtils() {} + /** * Retrieve all index names */ @@ -439,6 +442,13 @@ public static List getSequenceInformation( Dialect dialect = dialectResolver.resolveDialect( new DatabaseMetaDataDialectResolutionInfoAdapter(connection.getMetaData())); + // Attempt to use the Community Resolver + if (dialect == null) { + DialectResolver communityDialectResolver = new CommunityDialectResolver(); + dialect = communityDialectResolver.resolveDialect( + new DatabaseMetaDataDialectResolutionInfoAdapter(connection.getMetaData())); + } + List sequenceInformation = new ArrayList<>(); if (dialect.getSequenceSupport().supportsSequences()) { @@ -450,7 +460,7 @@ public static List getSequenceInformation( sequenceInformationExtractor.extractMetadata(extractionContext); return StreamSupport.stream(sequenceInformationIterator.spliterator(), false) - .collect(Collectors.toList()); + .toList(); } return sequenceInformation; } catch (SQLException e) { @@ -700,7 +710,8 @@ public QualifiedObjectNameFormatter getQualifiedObjectNameFormatter() { @Override public IdentifierHelper getIdentifierHelper() { - return new NormalizingIdentifierHelperImpl(this, null, true, true, true, true, null, null, null); + return new NormalizingIdentifierHelperImpl( + this, null, true, true, true, true, true, null, null, null); } @Override diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProvider.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProvider.java index c9b6faa54508..39524684ebce 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProvider.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProvider.java @@ -483,17 +483,17 @@ public void exportPollStatus( myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(oo, response.getWriter()); response.getWriter().close(); break; - //noinspection DefaultNotLastCaseInSwitch + //noinspection DefaultNotLastCaseInSwitch default: // Deliberate fall through ourLog.warn( "Unrecognized status encountered: {}. Treating as BUILDING/SUBMITTED", info.getStatus().name()); - //noinspection fallthrough + //noinspection fallthrough case FINALIZE: case QUEUED: case IN_PROGRESS: - //noinspection deprecation - we need to support old jobs after upgrade. + //noinspection deprecation - we need to support old jobs after upgrade. case ERRORED: if (theRequestDetails.getRequestType() == RequestTypeEnum.DELETE) { handleDeleteRequest(theJobId, response, info.getStatus()); diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/WorkChunkStatusEnum.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/WorkChunkStatusEnum.java index b708e58786b3..e0b08ea4e5e7 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/WorkChunkStatusEnum.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/WorkChunkStatusEnum.java @@ -111,7 +111,7 @@ public Set getNextStates() { return EnumSet.of(POLL_WAITING, READY); case ERRORED: return EnumSet.of(IN_PROGRESS, FAILED, COMPLETED); - // terminal states + // terminal states case FAILED: case COMPLETED: default: diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java index 8c9af360ddf1..7809b14f8a6e 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java @@ -258,12 +258,7 @@ public BUNDLE transaction( } List entries = myVersionAdapter.getEntries(response); - for (int i = 0; i < entries.size(); i++) { - if (ElementUtil.isEmpty(entries.get(i))) { - entries.remove(i); - i--; - } - } + entries.removeIf(ElementUtil::isEmpty); return (BUNDLE) response; } @@ -344,7 +339,7 @@ private void handleTransactionCreateOrUpdateOutcome( theRes = theIdToPersistedOutcome.get(newId).getResource(); } - if (theOutcome.getCreated()) { + if (Boolean.TRUE.equals(theOutcome.getCreated())) { myVersionAdapter.setResponseStatus(theNewEntry, toStatusString(Constants.STATUS_HTTP_201_CREATED)); } else { myVersionAdapter.setResponseStatus(theNewEntry, toStatusString(Constants.STATUS_HTTP_200_OK)); @@ -766,9 +761,7 @@ private void setRequestPartitionHeaderIfEntryHasTheExtension(IBase theReqEntry, Optional> partitionIdsExtensionOptional = myVersionAdapter.getEntryRequestExtensionByUrl(theReqEntry, EXTENSION_TRANSACTION_ENTRY_PARTITION_IDS); if (partitionIdsExtensionOptional.isPresent() - && partitionIdsExtensionOptional.get().getValue() instanceof IPrimitiveType) { - IPrimitiveType valueAsPrimitiveType = - (IPrimitiveType) partitionIdsExtensionOptional.get().getValue(); + && partitionIdsExtensionOptional.get().getValue() instanceof IPrimitiveType valueAsPrimitiveType) { String value = valueAsPrimitiveType.getValueAsString(); theRequestDetails.setHeaders(Constants.HEADER_X_REQUEST_PARTITION_IDS, List.of(value)); } @@ -1146,7 +1139,7 @@ private void consolidateDuplicateConditionals( } break; - // Conditional CREATE + // Conditional CREATE case "POST": conditionalUrl = ifNoneExist; if (isNotBlank(ifNoneExist)) { diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/util/LogbackTestExtension.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/util/LogbackTestExtension.java index e7f5faa6aa73..7fc77aba5c0c 100644 --- a/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/util/LogbackTestExtension.java +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/util/LogbackTestExtension.java @@ -140,7 +140,7 @@ public void setLoggerLevel(Level theLevel) { /** * @deprecated Use {@link #setLoggerLevel(Level)} instead */ - @Deprecated + @Deprecated(forRemoval = true) public void setUp(Level theLevel) { setLoggerLevel(theLevel); } @@ -149,7 +149,7 @@ public void setUp(Level theLevel) { * @deprecated This class should be registered as a junit5 extension, and will be set * up automatically. */ - @Deprecated + @Deprecated(forRemoval = true) public void setUp() { // nothing } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java index 14207ed940cf..2700273d2835 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java @@ -357,7 +357,7 @@ private static BaseConceptProperty createConceptPropertyDstu3( conceptProperty = new CodingConceptProperty(theName, coding.getSystem(), coding.getCode(), coding.getDisplay()); break; - // TODO: add other property types as per FHIR spec https://github.com/hapifhir/hapi-fhir/issues/5699 + // TODO: add other property types as per FHIR spec https://github.com/hapifhir/hapi-fhir/issues/5699 default: // other types will not fail for Remote Terminology conceptProperty = new StringConceptProperty(theName, theValue.toString()); @@ -490,7 +490,7 @@ private static BaseConceptProperty createConceptPropertyR4(final String theName, conceptProperty = new CodingConceptProperty(theName, coding.getSystem(), coding.getCode(), coding.getDisplay()); break; - // TODO: add other property types as per FHIR spec https://github.com/hapifhir/hapi-fhir/issues/5699 + // TODO: add other property types as per FHIR spec https://github.com/hapifhir/hapi-fhir/issues/5699 default: // other types will not fail for Remote Terminology conceptProperty = new StringConceptProperty(theName, theValue.toString()); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FHIRPathResourceGeneratorR4.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FHIRPathResourceGeneratorR4.java index f8c01b69edef..4435e6ee2f73 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FHIRPathResourceGeneratorR4.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FHIRPathResourceGeneratorR4.java @@ -342,7 +342,7 @@ private void handleFunctionNode(ExpressionNode fhirPath) { case Type: case Union: case Upper: - // TODO: unimplemented, what to do? + // TODO: unimplemented, what to do? case ConvertsToDate: break; case Round: diff --git a/hapi-tinder-plugin/.gitignore b/hapi-tinder-plugin/.gitignore index 4c86414cde70..baa523c33a2e 100644 --- a/hapi-tinder-plugin/.gitignore +++ b/hapi-tinder-plugin/.gitignore @@ -1,128 +1,127 @@ -target/ -/bin - -# Created by https://www.gitignore.io - -### Java ### -*.class - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.ear - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* - - -### Maven ### -target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.versionsBackup -pom.xml.next -release.properties -dependency-reduced-pom.xml -buildNumber.properties - - -### Vim ### -[._]*.s[a-w][a-z] -[._]s[a-w][a-z] -*.un~ -Session.vim -.netrwhist -*~ - - -### Intellij ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm - -*.iml - -## Directory-based project format: -.idea/ -# if you remove the above rule, at least ignore the following: - -# User-specific stuff: -# .idea/workspace.xml -# .idea/tasks.xml -# .idea/dictionaries - -# Sensitive or high-churn files: -# .idea/dataSources.ids -# .idea/dataSources.xml -# .idea/sqlDataSources.xml -# .idea/dynamic.xml -# .idea/uiDesigner.xml - -# Gradle: -# .idea/gradle.xml -# .idea/libraries - -# Mongo Explorer plugin: -# .idea/mongoSettings.xml - -## File-based project format: -*.ipr -*.iws - -## Plugin-specific files: - -# IntelliJ -/out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties - - - -### Eclipse ### -*.pydevproject -.metadata -.gradle -bin/ -tmp/ -*.tmp -*.bak -*.swp -*~.nib -local.properties -.settings/ -.loadpath - -# Eclipse Core -.project - -# External tool builders -.externalToolBuilders/ - -# Locally stored "Eclipse launch configurations" -*.launch - -# CDT-specific -.cproject - -# JDT-specific (Eclipse Java Development Tools) -.classpath - -# PDT-specific -.buildpath - -# sbteclipse plugin -.target - -# TeXlipse plugin -.texlipse - +target/ +/bin + +# Created by https://www.gitignore.io + +### Java ### +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties + + +### Vim ### +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ + + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + + + +### Eclipse ### +*.pydevproject +.metadata +.gradle +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# PDT-specific +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index bafa6c98c7e9..a9ee49f2abe5 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -123,11 +123,6 @@ org.apache.velocity velocity-engine-core - - commons-lang - commons-lang - 2.6 - com.google.guava @@ -170,7 +165,8 @@ org.apache.maven maven-core - 3.9.9 + 3.9.11 + provided org.apache.maven @@ -187,7 +183,7 @@ org.apache.ant ant - 1.10.11 + 1.10.15 @@ -201,7 +197,7 @@ commons-codec commons-codec - 1.15 + 1.19.0 @@ -220,6 +216,7 @@ com.h2database h2 + test diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java index 05760bd7260e..205be8cc82f3 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java @@ -48,8 +48,6 @@ public class FhirDstu2 implements IFhirVersion { - private String myId; - @Override public IFhirPath createFhirPathExecutor(FhirContext theFhirContext) { throw new UnsupportedOperationException(Msg.code(74) + "FluentPath is not supported in DSTU2 contexts"); @@ -59,9 +57,7 @@ public IFhirPath createFhirPathExecutor(FhirContext theFhirContext) { public IResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition, String theServerBase) { StructureDefinition retVal = new StructureDefinition(); - RuntimeResourceDefinition def = theRuntimeResourceDefinition; - - myId = def.getId(); + String myId = theRuntimeResourceDefinition.getId(); if (StringUtils.isBlank(myId)) { myId = theRuntimeResourceDefinition.getName().toLowerCase(); } diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/Configuration.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/Configuration.java index 640bde8c9943..3335f3e4f136 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/Configuration.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/Configuration.java @@ -3,7 +3,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.tinder.parser.BaseStructureSpreadsheetParser; +import ca.uhn.fhir.tinder.parser.BaseStructureParser; import org.apache.commons.text.WordUtils; import java.io.File; @@ -17,16 +17,15 @@ public class Configuration { private final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Configuration.class); - private String version; - private File targetDirectory; + private final String version; + private final File targetDirectory; private String packageSuffix; - private String packageBase; - private FhirContext fhirContext; - private File packageDirectoryBase; + private final String packageBase; + private final File packageDirectoryBase; private final List resourceNames = new ArrayList<>(); - private String baseDir; + private final String baseDir; public Configuration( String version, @@ -37,27 +36,10 @@ public Configuration( List excludeResourceNames) { this.targetDirectory = targetDirectory; this.packageBase = packageBase; + this.baseDir = baseDir; this.packageDirectoryBase = new File(targetDirectory, packageBase.replace(".", File.separatorChar + "")); - switch (version) { - case "dstu2": - fhirContext = FhirContext.forDstu2(); - break; - case "dstu3": - fhirContext = FhirContext.forDstu3(); - packageSuffix = ".dstu3"; - break; - case "r4": - fhirContext = FhirContext.forR4(); - packageSuffix = ".r4"; - break; - case "r5": - fhirContext = FhirContext.forR5(); - packageSuffix = ".r5"; - break; - default: - throw new IllegalArgumentException(Msg.code(92) + "Unknown version configured: " + version); - } + FhirContext fhirContext = getFhirContext(version); this.version = version; if (baseResourceNames == null || baseResourceNames.isEmpty()) { @@ -74,7 +56,7 @@ public Configuration( ourLog.debug("Property file contains: {}", p); - TreeSet keys = new TreeSet(); + TreeSet keys = new TreeSet<>(); for (Object next : p.keySet()) { keys.add((String) next); } @@ -123,7 +105,7 @@ public String getVersion() { } public String getResourcePackage() { - if (BaseStructureSpreadsheetParser.determineVersionEnum(version).isRi()) { + if (BaseStructureParser.determineVersionEnum(version).isRi()) { return "org.hl7.fhir." + version + ".model"; } return "ca.uhn.fhir.model." + version + ".resource"; @@ -144,4 +126,28 @@ public File getTargetDirectory() { public String getBaseDir() { return baseDir; } + + private FhirContext getFhirContext(String version) { + FhirContext fhirContext; + switch (version) { + case "dstu2": + fhirContext = FhirContext.forDstu2(); + break; + case "dstu3": + fhirContext = FhirContext.forDstu3(); + packageSuffix = ".dstu3"; + break; + case "r4": + fhirContext = FhirContext.forR4(); + packageSuffix = ".r4"; + break; + case "r5": + fhirContext = FhirContext.forR5(); + packageSuffix = ".r5"; + break; + default: + throw new IllegalArgumentException(Msg.code(92) + "Unknown version configured: " + version); + } + return fhirContext; + } } diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/model/ValueSetTm.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/model/ValueSetTm.java index abb1792964c4..863dfb84a24f 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/model/ValueSetTm.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/model/ValueSetTm.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.tinder.model; -import org.codehaus.plexus.util.StringUtils; +import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.Collections; @@ -16,7 +16,7 @@ public class ValueSetTm { private String myDescription; private String myId; private String myName; - private Set myCodeValues = new HashSet(); + private final Set myCodeValues = new HashSet(); public void addConcept(String theSystem, String theCode, String theText, String theDefinition) { String key = theSystem + "|" + theCode; @@ -35,8 +35,8 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; ValueSetTm other = (ValueSetTm) obj; - String id1 = myId != null && myId.isEmpty() == false ? myId : myName; - String id2 = other.myId != null && other.myId.isEmpty() == false ? other.myId : other.myName; + String id1 = myId != null && !myId.isEmpty() ? myId : myName; + String id2 = other.myId != null && !other.myId.isEmpty() ? other.myId : other.myName; id1 = StringUtils.defaultString(id1); id2 = StringUtils.defaultString(id2); return id1.equals(id2); @@ -92,10 +92,10 @@ public void setName(String theName) { public class Code { - private String myCode; - private String myDefinition; - private String myDisplay; - private String mySystem; + private final String myCode; + private final String myDefinition; + private final String myDisplay; + private final String mySystem; private Code(String theSystem, String theCode, String theDisplay, String theDefinition) { mySystem = theSystem; @@ -131,8 +131,7 @@ public String getCodeEnumValue() { newValue = newValue.substring(0, newValue.length() - 1); } ourLog.info( - "[{}] Replacing numeric code {} with description: {}", - new Object[] {myName, retVal, newValue}); + "[{}] Replacing numeric code {} with description: {}", myName, retVal, newValue); retVal = newValue; } } diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/BaseStructureSpreadsheetParser.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/BaseStructureSpreadsheetParser.java index 23fa7cbb9d52..4c07dd9c3436 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/BaseStructureSpreadsheetParser.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/BaseStructureSpreadsheetParser.java @@ -63,7 +63,7 @@ public BaseStructureSpreadsheetParser(String theVersion, String theBaseDir) thro public void parse() throws Exception { - myNameToValueSetUrl = new HashMap(); + myNameToValueSetUrl = new HashMap<>(); if (getVersion().equals("dstu2")) { ourLog.info("Loading ValueSets..."); FhirContext ctx = FhirContext.forDstu2(); @@ -124,9 +124,6 @@ public void parse() throws Exception { Map elements = new HashMap(); elements.put(resource.getElementName(), resource); - // Map blockFullNameToShortName = new - // HashMap(); - List blockCopies = new ArrayList(); for (int i = 2; i < rows.getLength(); i++) { Element nextRow = (Element) rows.item(i); diff --git a/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IBase.java b/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IBase.java index 97989819402c..61d5d77b7976 100644 --- a/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IBase.java +++ b/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IBase.java @@ -17,7 +17,7 @@ * limitations under the License. * #L% */ -package org.hl7.fhir.dstu2.model; +package org.hl7.fhir.instance.model; /** * This interface is a simple marker for anything which is an HL7 diff --git a/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IBaseResource.java b/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IBaseResource.java index 4f1c5dff580a..939658f42577 100644 --- a/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IBaseResource.java +++ b/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IBaseResource.java @@ -17,11 +17,11 @@ * limitations under the License. * #L% */ -package org.hl7.fhir.dstu2.model; +package org.hl7.fhir.instance.model; /** * For now, this is a simple marker interface indicating that a class is a resource type. - * There are two concrete types of implementations of this interrface. The first are + * There are two concrete types of implementations of this interface. The first are * HL7.org's Resource structures (e.g. * org.hl7.fhir.instance.model.Patient) and * the second are HAPI's Resource structures, e.g. diff --git a/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IIdType.java b/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IIdType.java index 7c3546ee0728..4af47e9fcc4c 100644 --- a/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IIdType.java +++ b/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IIdType.java @@ -17,7 +17,7 @@ * limitations under the License. * #L% */ -package org.hl7.fhir.dstu2.model; +package org.hl7.fhir.instance.model; public interface IIdType extends IBase { diff --git a/hapi-tinder-test/.gitignore b/hapi-tinder-test/.gitignore index 48875ee9bfb6..05dc0fec6f14 100644 --- a/hapi-tinder-test/.gitignore +++ b/hapi-tinder-test/.gitignore @@ -126,4 +126,3 @@ local.properties # TeXlipse plugin .texlipse - diff --git a/pom.xml b/pom.xml index d65683695a96..fdcebd979226 100644 --- a/pom.xml +++ b/pom.xml @@ -128,11 +128,6 @@ junit-jupiter-engine test - - org.mockito - mockito-core - test - org.mockito mockito-junit-jupiter @@ -991,10 +986,10 @@ 6.6.7 - 2.41.1 + 2.46.1 **/test/**/*.java -Dfile.encoding=UTF-8 -Xmx2048m - 3.5.2 + 3.5.4 @@ -1009,29 +1004,29 @@ 1.2.0 4.2.5 1.2 - 3.1.8 - 10.21.1 + 3.2.2 + 10.26.1 2.12.1 - 1.15 - 1.26.0 + 1.19.0 + 1.28.0 - 2.11.0 + 2.13.0 - 2.12.0 - 1.10.0 - 2.17.0 + 2.12.1 + 1.14.0 + 2.20.0 3.18.0 1.2 - 2.36.0 - 5.18.0 + 2.41.0 + 5.19.0 0.7.9 - 33.2.1-jre - 2.12.0 + 33.5.0-jre + 2.13.2 2.2.11_1 2.3.1 2.3.0.1 @@ -1040,62 +1035,61 @@ 3.0.3 12.0.17 3.0.2 - 5.10.1 + 5.13.4 + 1.13.4 0.64.8 - 10.20.1 - 6.6.4.Final - 1.5.16 + 11.13.1 + 7.1.1.Final + 1.5.18 - 7.2.1.Final + 8.1.2.Final - 9.11.1 + 9.12.2 2.2 - 8.0.0.Final + 9.0.1.Final 4.4.16 4.5.14 2.20.0 2.20.0 2.20 - 5.4.3 - 5.3.1 - 3.3.0 - 1.8 + 5.5 + 5.3.5 4.12.0 - 2.10.0 + 2.20.0 ${otel_instrumentation.version}-alpha 4.1.2 1.4 - 6.2.10.Final + 6.2.12.Final 7.1.2 9.5.4 2.13.0 0.9.11 9.8.0-15 1.2_5 - 2.2.30 - 2.0.16 - 2.24.1 - 6.1.21 - 2024.0.5 - 6.1.15 - 3.3.11 - 2.0.10 + 2.2.37 + 2.0.17 + 2.25.1 + 6.2.11 + 2025.0.4 + 6.2.11 + 3.5.6 + 2.0.12 2.9.0 3.6.1.Final 3.1.4 - 1.20.3 - 3.1.2.RELEASE + 1.21.3 + 3.1.3.RELEASE 4.4.1 UTF-8 1.0.1 1.52.0 8.15.3 - 1.0.9 + 1.0.10 5.4.1 @@ -1110,12 +1104,12 @@ - 3.3.0 + 3.3.1 3.2.1 - 0.8.12 + 0.8.13 3.7.1 2.5.0 - 3.5.0 + 3.6.1 3.6.0 @@ -1555,7 +1549,7 @@ com.mysql mysql-connector-j - 9.1.0 + 9.4.0 org.springdoc @@ -1642,11 +1636,6 @@ fluent-hc ${httpclient_version} - - org.apache.httpcomponents - httpclient-android - 4.3.5.1 - org.apache.httpcomponents httpcore @@ -1777,17 +1766,12 @@ org.codehaus.plexus plexus-utils - 3.1.0 + 3.6.0 com.fasterxml.woodstox woodstox-core - 6.4.0 - - - org.ebaysf.web - cors-filter - ${ebay_cors_filter_version} + 6.7.0 org.eclipse.jetty.ee10 @@ -1997,6 +1981,12 @@ hibernate-envers ${hibernate_version} + + + org.hibernate.orm + hibernate-community-dialects + ${hibernate_version} + org.hibernate.validator hibernate-validator @@ -2021,7 +2011,7 @@ org.elasticsearch elasticsearch - 8.9.2 + 9.1.4 org.hibernate.search @@ -2074,15 +2064,21 @@ ${junit_version} test + + org.junit.platform + junit-platform-launcher + ${junit_platform_version} + test + org.junit-pioneer junit-pioneer - 1.3.8 + 2.3.0 org.mariadb.jdbc mariadb-java-client - 3.0.4 + 3.5.6 org.mockito @@ -2097,17 +2093,17 @@ org.postgresql postgresql - 42.7.7 + 42.7.8 com.oracle.database.jdbc ojdbc11 - 23.6.0.24.10 + 23.9.0.25.07 org.quartz-scheduler quartz - 2.3.2 + 2.5.0 com.zaxxer @@ -2514,12 +2510,17 @@ org.apache.maven.plugins maven-clean-plugin - 3.4.0 + 3.5.0 + + + org.apache.maven.plugins + maven-deploy-plugin + 3.1.4 org.apache.maven.plugins maven-compiler-plugin - 3.14.0 + 3.14.1 UTF-8 true @@ -2545,17 +2546,20 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.11.2 + 3.12.0 + + + org.apache.maven.plugins maven-jar-plugin - 3.2.0 + 3.4.2 org.apache.maven.plugins maven-jxr-plugin - 3.0.0 + 3.6.0 org.apache.maven.plugins @@ -2577,12 +2581,12 @@ org.apache.maven.plugins maven-plugin-plugin - 3.9.0 + 3.15.1 org.apache.maven.plugins maven-shade-plugin - 3.6.0 + 3.6.1 org.apache.maven.plugins @@ -2618,7 +2622,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.6.0 + 3.6.1 org.codehaus.mojo @@ -2638,7 +2642,7 @@ org.codehaus.mojo versions-maven-plugin - 2.18.0 + 2.19.0 false @@ -2672,7 +2676,7 @@ org.apache.maven.plugins maven-install-plugin - 3.1.3 + 3.1.4 org.codehaus.mojo