API improvements for MegaScale (#4112)

* Start work on sharded DB

* Work on migrating

* Work

* Ongoing work

* Ongoing work

* Work

* Work

* Ongoing work

* Work

* Work

* Test fix

* Test fix

* Work

* Compile fix

* Test cleanup

* Test fix

* Resolve fixmes

* Resolve fixme

* Resolve compile errors

* Compile fix

* Fixes

* Work

* Add note for later

* Test fix

* Test fixes

* Build fix

* Test fixes

* Fixes

* Test fixes

* Test fixes

* Test fixes

* Test fix

* Fixes

* Fixes

* Remove dead code in pom

* More test cleanup

* Docs fix

* Changes

* Fix

* Fixes

* Resolve merge conflicts

* Build update

* Merge build fixes

* Version bump

* Address review comments

* Review comments

* Review comments

* Test fixes

* Test fix

* Cleanup
This commit is contained in:
James Agnew 2022-12-20 12:05:15 -05:00 committed by GitHub
parent 155b579093
commit 0d5f8da93f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
213 changed files with 1920 additions and 1068 deletions

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -111,6 +111,7 @@
<ignoredResourcePattern>.*\.txt$</ignoredResourcePattern>
<ignoredResourcePattern>.*\.html$</ignoredResourcePattern>
<ignoredResourcePattern>schemaorg_apache_xmlbeans.*</ignoredResourcePattern>
<ignoredResource>classpath.index</ignoredResource>
<ignoredResource>javac.bat</ignoredResource>
<ignoredResource>about.html</ignoredResource>
<ignoredResource>changelog.xml</ignoredResource>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -1898,6 +1898,8 @@ public enum Pointcut implements IPointcut {
* <p>
* Hooks must return an instance of <code>ca.uhn.fhir.interceptor.model.RequestPartitionId</code>.
* </p>
*
* @see #STORAGE_PARTITION_IDENTIFY_ANY For an alternative that is not read/write specific
*/
STORAGE_PARTITION_IDENTIFY_CREATE(
// Return type
@ -1938,6 +1940,8 @@ public enum Pointcut implements IPointcut {
* <p>
* Hooks must return an instance of <code>ca.uhn.fhir.interceptor.model.RequestPartitionId</code>.
* </p>
*
* @see #STORAGE_PARTITION_IDENTIFY_ANY For an alternative that is not read/write specific
*/
STORAGE_PARTITION_IDENTIFY_READ(
// Return type
@ -1948,6 +1952,95 @@ public enum Pointcut implements IPointcut {
"ca.uhn.fhir.interceptor.model.ReadPartitionIdRequestDetails"
),
/**
* <b>Storage Hook:</b>
* Invoked before FHIR operations to request the identification of the partition ID to be associated with the
* request being made.
* <p>
* This hook is an alternative to {@link #STORAGE_PARTITION_IDENTIFY_READ} and {@link #STORAGE_PARTITION_IDENTIFY_CREATE}
* and can be used in cases where a partition interceptor does not need knowledge of the specific resources being
* accessed/read/written in order to determine the appropriate partition.
* </p>
* <p>
* This hook will only be called if
* partitioning is enabled in the JPA server.
* </p>
* <p>
* Hooks may accept the following parameters:
* </p>
* <ul>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request. Note that the bean
* properties are not all guaranteed to be populated, depending on how early during processing the
* exception occurred.
* </li>
* <li>
* ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li>
* </ul>
* <p>
* Hooks must return an instance of <code>ca.uhn.fhir.interceptor.model.RequestPartitionId</code>.
* </p>
*
* @see #STORAGE_PARTITION_IDENTIFY_READ
* @see #STORAGE_PARTITION_IDENTIFY_CREATE
*/
STORAGE_PARTITION_IDENTIFY_ANY(
// Return type
"ca.uhn.fhir.interceptor.model.RequestPartitionId",
// Params
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
),
/**
* <b>Storage Hook:</b>
* Invoked when a partition has been created, typically meaning the <code>$partition-management-create-partition</code>
* operation has been invoked.
* <p>
* This hook will only be called if
* partitioning is enabled in the JPA server.
* </p>
* <p>
* Hooks may accept the following parameters:
* </p>
* <ul>
* <li>
* ca.uhn.fhir.interceptor.model.RequestPartitionId - The partition ID that was selected
* </li>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request. Note that the bean
* properties are not all guaranteed to be populated, depending on how early during processing the
* exception occurred.
* </li>
* <li>
* ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li>
* </ul>
* <p>
* Hooks must return void.
* </p>
*/
STORAGE_PARTITION_CREATED(
// Return type
void.class,
// Params
"ca.uhn.fhir.interceptor.model.RequestPartitionId",
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
),
/**
* <b>Storage Hook:</b>
* Invoked before any partition aware FHIR operation, when the selected partition has been identified (ie. after the

View File

@ -60,6 +60,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;
// TODO: JA maybe add an enummap for pointcuts registered?
public abstract class BaseInterceptorService<POINTCUT extends IPointcut> implements IBaseInterceptorService<POINTCUT>, IBaseInterceptorBroadcaster<POINTCUT> {
private static final Logger ourLog = LoggerFactory.getLogger(BaseInterceptorService.class);
private final List<Object> myInterceptors = new ArrayList<>();

View File

@ -4,14 +4,14 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-bom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<packaging>pom</packaging>
<name>HAPI FHIR BOM</name>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -12,7 +12,7 @@ HAPI-FHIR 6.0.0 has begun the process of replacing Spring Batch with a custom ba
A HAPI-FHIR batch job definition consists of a job name, version, parameter json input type, and a chain of job steps. Each step of the chain declares the json output type it produces, which will be the input type for the following step. The final step in the chain does not declare an output type as the final step will typically do the work of the job, e.g. reindex resources, export data to disk, etc.
<a href="/hapi-fhir/docs/images/job-definition.svg"/>
<img src="/hapi-fhir/docs/images/job-definition.svg"/>
### Submitting a Job

View File

@ -11,7 +11,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -20,75 +20,12 @@ package ca.uhn.fhir.jpa.util;
* #L%
*/
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ClassInfo;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import org.hibernate.annotations.Subselect;
import org.hibernate.validator.constraints.Length;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.InstantType;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.GeneratedValue;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Size;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static com.google.common.base.Ascii.toUpperCase;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class TestUtil {
public static final int MAX_COL_LENGTH = 4000;
private static final int MAX_LENGTH = 30;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestUtil.class);
private static Set<String> ourReservedWords;
// Exceptions set because H2 sets indexes for FKs automatically so this index had to be called as the target FK field
// it is indexing to avoid SchemaMigrationTest to complain about the extra index (which doesn't exist in H2)
private static final Set<String> duplicateNameValidationExceptionList = Sets.newHashSet(
"FK_CONCEPTPROP_CONCEPT",
"FK_CONCEPTDESIG_CONCEPT",
"FK_TERM_CONCEPTPC_CHILD",
"FK_TERM_CONCEPTPC_PARENT",
"FK_TRM_VALUESET_CONCEPT_PID",
"FK_SEARCHINC_SEARCH"
);
/**
* non instantiable
@ -97,288 +34,6 @@ public class TestUtil {
super();
}
/**
* This is really only useful for unit tests, do not call otherwise
*/
@SuppressWarnings("UnstableApiUsage")
public static void scanEntities(String packageName) throws IOException, ClassNotFoundException {
try (InputStream is = TestUtil.class.getResourceAsStream("/mysql-reserved-words.txt")) {
String contents = IOUtils.toString(is, Constants.CHARSET_UTF8);
String[] words = contents.split("\\n");
ourReservedWords = Arrays.stream(words)
.filter(t -> isNotBlank(t))
.map(t -> toUpperCase(t))
.collect(Collectors.toSet());
}
ImmutableSet<ClassInfo> classes = ClassPath.from(TestUtil.class.getClassLoader()).getTopLevelClassesRecursive(packageName);
Set<String> names = new HashSet<String>();
if (classes.size() <= 1) {
throw new InternalErrorException(Msg.code(1623) + "Found no classes");
}
for (ClassInfo classInfo : classes) {
Class<?> clazz = Class.forName(classInfo.getName());
Entity entity = clazz.getAnnotation(Entity.class);
Embeddable embeddable = clazz.getAnnotation(Embeddable.class);
if (entity == null && embeddable == null) {
continue;
}
scanClass(names, clazz, false);
}
}
private static void scanClass(Set<String> theNames, Class<?> theClazz, boolean theIsSuperClass) {
Map<String, Integer> columnNameToLength = new HashMap<>();
scanClassOrSuperclass(theNames, theClazz, theIsSuperClass, columnNameToLength);
Table table = theClazz.getAnnotation(Table.class);
if (table != null) {
// This is the length for MySQL per https://dev.mysql.com/doc/refman/8.0/en/innodb-limits.html
// No idea why 3072.. what a weird limit but I'm sure they have their reason.
int maxIndexLength = 3072;
for (UniqueConstraint nextIndex : table.uniqueConstraints()) {
int indexLength = calculateIndexLength(nextIndex.columnNames(), columnNameToLength, theClazz, nextIndex.name());
if (indexLength > maxIndexLength) {
throw new IllegalStateException(Msg.code(1624) + "Index '" + nextIndex.name() + "' is too long. Length is " + indexLength + " and must not exceed " + maxIndexLength + " which is the maximum MySQL length");
}
}
}
}
private static int calculateIndexLength(String[] theColumnNames, Map<String, Integer> theColumnNameToLength, Class<?> theClazz, String theIndexName) {
int retVal = 0;
for (String nextName : theColumnNames) {
Integer nextLength = theColumnNameToLength.get(nextName);
if (nextLength == null) {
throw new IllegalStateException(Msg.code(1625) + "Index '" + theIndexName + "' references unknown column: " + nextName);
}
retVal += nextLength;
}
return retVal;
}
private static void scanClassOrSuperclass(Set<String> theNames, Class<?> theClazz, boolean theIsSuperClass, Map<String, Integer> columnNameToLength) {
ourLog.info("Scanning: {}", theClazz.getSimpleName());
Subselect subselect = theClazz.getAnnotation(Subselect.class);
boolean isView = (subselect != null);
scan(theClazz, theNames, theIsSuperClass, isView);
for (Field nextField : theClazz.getDeclaredFields()) {
if (Modifier.isStatic(nextField.getModifiers())) {
continue;
}
ourLog.info(" * Scanning field: {}", nextField.getName());
scan(nextField, theNames, theIsSuperClass, isView);
Lob lobClass = nextField.getAnnotation(Lob.class);
if (lobClass != null) {
if (nextField.getType().equals(byte[].class) == false) {
//Validate.isTrue(false);
}
}
boolean isTransient = nextField.getAnnotation(Transient.class) != null;
if (!isTransient) {
boolean hasColumn = nextField.getAnnotation(Column.class) != null;
boolean hasJoinColumn = nextField.getAnnotation(JoinColumn.class) != null;
boolean hasEmbeddedId = nextField.getAnnotation(EmbeddedId.class) != null;
boolean hasEmbedded = nextField.getAnnotation(Embedded.class) != null;
OneToMany oneToMany = nextField.getAnnotation(OneToMany.class);
OneToOne oneToOne = nextField.getAnnotation(OneToOne.class);
boolean isOtherSideOfOneToManyMapping = oneToMany != null && isNotBlank(oneToMany.mappedBy());
boolean isOtherSideOfOneToOneMapping = oneToOne != null && isNotBlank(oneToOne.mappedBy());
boolean isField = nextField.getAnnotation(org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField.class) != null;
isField |= nextField.getAnnotation(org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField.class) != null;
isField |= nextField.getAnnotation(org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumberField.class) != null;
Validate.isTrue(
hasEmbedded ||
hasColumn ||
hasJoinColumn ||
isOtherSideOfOneToManyMapping ||
isOtherSideOfOneToOneMapping ||
hasEmbeddedId ||
isField, "Non-transient has no @Column or @JoinColumn or @EmbeddedId: " + nextField);
int columnLength = 16;
String columnName = null;
if (hasColumn) {
columnName = nextField.getAnnotation(Column.class).name();
columnLength = nextField.getAnnotation(Column.class).length();
}
if (hasJoinColumn) {
columnName = nextField.getAnnotation(JoinColumn.class).name();
}
if (columnName != null) {
if (nextField.getType().isAssignableFrom(String.class)) {
// MySQL treats each char as the max possible byte count in UTF-8 for its calculations
columnLength = columnLength * 4;
}
columnNameToLength.put(columnName, columnLength);
}
}
}
for (Class<?> innerClass : theClazz.getDeclaredClasses()) {
Embeddable embeddable = innerClass.getAnnotation(Embeddable.class);
if (embeddable != null) {
scanClassOrSuperclass(theNames, innerClass, false, columnNameToLength);
}
}
if (theClazz.getSuperclass().equals(Object.class)) {
return;
}
scanClassOrSuperclass(theNames, theClazz.getSuperclass(), true, columnNameToLength);
}
private static void scan(AnnotatedElement theAnnotatedElement, Set<String> theNames, boolean theIsSuperClass, boolean theIsView) {
Table table = theAnnotatedElement.getAnnotation(Table.class);
if (table != null) {
// Banned name because we already used it once
ArrayList<String> bannedNames = Lists.newArrayList("CDR_USER_2FA", "TRM_VALUESET_CODE");
Validate.isTrue(!bannedNames.contains(table.name().toUpperCase()));
Validate.isTrue(table.name().toUpperCase().equals(table.name()));
assertNotADuplicateName(table.name(), theNames);
for (UniqueConstraint nextConstraint : table.uniqueConstraints()) {
assertNotADuplicateName(nextConstraint.name(), theNames);
Validate.isTrue(nextConstraint.name().startsWith("IDX_"), nextConstraint.name() + " must start with IDX_");
}
for (Index nextConstraint : table.indexes()) {
assertNotADuplicateName(nextConstraint.name(), theNames);
Validate.isTrue(nextConstraint.name().startsWith("IDX_") || nextConstraint.name().startsWith("FK_"),
nextConstraint.name() + " must start with IDX_ or FK_ (last one when indexing a FK column)");
}
}
JoinColumn joinColumn = theAnnotatedElement.getAnnotation(JoinColumn.class);
if (joinColumn != null) {
String columnName = joinColumn.name();
validateColumnName(columnName, theAnnotatedElement);
assertNotADuplicateName(columnName, null);
ForeignKey fk = joinColumn.foreignKey();
if (theIsSuperClass) {
Validate.isTrue(isBlank(fk.name()), "Foreign key on " + theAnnotatedElement + " has a name() and should not as it is a superclass");
} else {
Validate.notNull(fk);
Validate.isTrue(isNotBlank(fk.name()), "Foreign key on " + theAnnotatedElement + " has no name()");
// Validate FK naming.
// temporarily allow two hibernate legacy sp fk names until we fix them
List<String> legacySPHibernateFKNames = Arrays.asList(
"FKC97MPK37OKWU8QVTCEG2NH9VN", "FKGXSREUTYMMFJUWDSWV3Y887DO");
Validate.isTrue(fk.name().startsWith("FK_") || legacySPHibernateFKNames.contains(fk.name()),
"Foreign key " + fk.name() + " on " + theAnnotatedElement + " must start with FK_");
if ( ! duplicateNameValidationExceptionList.contains(fk.name())) {
assertNotADuplicateName(fk.name(), theNames);
}
}
}
Column column = theAnnotatedElement.getAnnotation(Column.class);
if (column != null) {
String columnName = column.name();
validateColumnName(columnName, theAnnotatedElement);
assertNotADuplicateName(columnName, null);
Validate.isTrue(column.unique() == false, "Should not use unique attribute on column (use named @UniqueConstraint instead) on " + theAnnotatedElement);
boolean hasLob = theAnnotatedElement.getAnnotation(Lob.class) != null;
Field field = (Field) theAnnotatedElement;
/*
* For string columns, we want to make sure that an explicit max
* length is always specified, and that this max is always sensible.
* Unfortunately there is no way to differentiate between "explicitly
* set to 255" and "just using the default of 255" so we have banned
* the exact length of 255.
*/
if (field.getType().equals(String.class)) {
if (!hasLob) {
if (!theIsView && column.length() == 255) {
throw new IllegalStateException(Msg.code(1626) + "Field does not have an explicit maximum length specified: " + field);
}
if (column.length() > MAX_COL_LENGTH) {
throw new IllegalStateException(Msg.code(1627) + "Field is too long: " + field);
}
}
Size size = theAnnotatedElement.getAnnotation(Size.class);
if (size != null) {
if (size.max() > MAX_COL_LENGTH) {
throw new IllegalStateException(Msg.code(1628) + "Field is too long: " + field);
}
}
Length length = theAnnotatedElement.getAnnotation(Length.class);
if (length != null) {
if (length.max() > MAX_COL_LENGTH) {
throw new IllegalStateException(Msg.code(1629) + "Field is too long: " + field);
}
}
}
}
GeneratedValue gen = theAnnotatedElement.getAnnotation(GeneratedValue.class);
SequenceGenerator sg = theAnnotatedElement.getAnnotation(SequenceGenerator.class);
Validate.isTrue((gen != null) == (sg != null));
if (gen != null) {
assertNotADuplicateName(gen.generator(), theNames);
assertNotADuplicateName(sg.name(), null);
assertNotADuplicateName(sg.sequenceName(), null);
assertEquals(gen.generator(), sg.name());
assertEquals(gen.generator(), sg.sequenceName());
}
}
private static void validateColumnName(String theColumnName, AnnotatedElement theElement) {
if (!theColumnName.equals(theColumnName.toUpperCase())) {
throw new IllegalArgumentException(Msg.code(1630) + "Column name must be all upper case: " + theColumnName + " found on " + theElement);
}
if (ourReservedWords.contains(theColumnName)) {
throw new IllegalArgumentException(Msg.code(1631) + "Column name is a reserved word: " + theColumnName + " found on " + theElement);
}
}
private static void assertEquals(String theGenerator, String theName) {
Validate.isTrue(theGenerator.equals(theName));
}
private static void assertNotADuplicateName(String theName, Set<String> theNames) {
if (isBlank(theName)) {
return;
}
Validate.isTrue(theName.length() <= MAX_LENGTH, "Identifier \"" + theName + "\" is " + theName.length() + " chars long");
if (theNames != null) {
Validate.isTrue(theNames.add(theName), "Duplicate name: " + theName);
}
}
public static InstantType getTimestamp(IBaseResource resource) {
return new InstantType(new Date(resource.getMeta().getLastUpdated().getTime()));

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -417,7 +417,7 @@
<plugins>
<plugin>
<groupId>de.jpdigital</groupId>
<artifactId>hibernate54-ddl-maven-plugin</artifactId>
<artifactId>hibernate56-ddl-maven-plugin</artifactId>
<configuration>
<dialects>
<param>h2</param>
@ -460,6 +460,11 @@
<artifactId>hapi-fhir-jpaserver-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate_version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>

View File

@ -35,7 +35,7 @@ import ca.uhn.fhir.jpa.entity.BulkExportJobEntity;
import ca.uhn.fhir.jpa.model.sched.HapiJob;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import org.hl7.fhir.instance.model.api.IIdType;

View File

@ -39,7 +39,7 @@ import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.jpa.dao.mdm.MdmExpansionCacheSvc;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.QueryChunker;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;

View File

@ -27,7 +27,7 @@ import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.QueryChunker;
import org.hl7.fhir.instance.model.api.IIdType;

View File

@ -231,7 +231,7 @@ public class JpaConfig {
@Lazy
@Bean
public ThreadSafeResourceDeleterSvc safeDeleter(DaoRegistry theDaoRegistry, IInterceptorBroadcaster theInterceptorBroadcaster, HapiTransactionService hapiTransactionService) {
return new ThreadSafeResourceDeleterSvc(theDaoRegistry, theInterceptorBroadcaster, hapiTransactionService.getTransactionManager());
return new ThreadSafeResourceDeleterSvc(theDaoRegistry, theInterceptorBroadcaster, hapiTransactionService);
}
@Lazy
@ -661,7 +661,7 @@ public class JpaConfig {
}
@Bean
public IIdHelperService idHelperService () {
public IIdHelperService idHelperService() {
return new IdHelperService();
}
@ -767,7 +767,7 @@ public class JpaConfig {
}
@Bean
public ISynchronousSearchSvc synchronousSearchSvc(){
public ISynchronousSearchSvc synchronousSearchSvc() {
return new SynchronousSearchSvcImpl();
}

View File

@ -38,8 +38,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@Import({
FhirContextDstu2Config.class,
GeneratedDaoAndResourceProviderConfigDstu2.class,
JpaConfig.class
GeneratedDaoAndResourceProviderConfigDstu2.class
})
public class JpaDstu2Config {
@Bean

View File

@ -31,6 +31,7 @@ import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.jpa.dao.data.IResourceSearchViewDao;
import ca.uhn.fhir.jpa.dao.data.IResourceTagDao;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
@ -107,6 +108,8 @@ public class SearchConfig {
private PersistedJpaBundleProviderFactory myPersistedJpaBundleProviderFactory;
@Autowired
private IRequestPartitionHelperSvc myRequestPartitionHelperService;
@Autowired
private HapiTransactionService myHapiTransactionService;
@Bean
public ISearchCoordinatorSvc searchCoordinatorSvc() {
@ -114,7 +117,7 @@ public class SearchConfig {
myContext,
myDaoConfig,
myInterceptorBroadcaster,
myManagedTxManager,
myHapiTransactionService,
mySearchCacheSvc,
mySearchResultCacheSvc,
myDaoRegistry,
@ -160,7 +163,7 @@ public class SearchConfig {
@Scope("prototype")
public SearchTask createSearchTask(SearchTaskParameters theParams) {
return new SearchTask(theParams,
myManagedTxManager,
myHapiTransactionService,
myContext,
myInterceptorBroadcaster,
mySearchBuilderFactory,
@ -176,7 +179,7 @@ public class SearchConfig {
@Scope("prototype")
public SearchContinuationTask createSearchContinuationTask(SearchTaskParameters theParams) {
return new SearchContinuationTask(theParams,
myManagedTxManager,
myHapiTransactionService,
myContext,
myInterceptorBroadcaster,
mySearchBuilderFactory,

View File

@ -1247,8 +1247,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
* @param entity the existing entity.
*/
private void failIfPartitionMismatch(RequestDetails theRequest, ResourceTable entity) {
if (myPartitionSettings.isPartitioningEnabled() && theRequest != null && theRequest.getTenantId() != null && entity.getPartitionId() != null &&
!ALL_PARTITIONS_NAME.equals(theRequest.getTenantId())) {
if (myPartitionSettings.isPartitioningEnabled() && theRequest != null && theRequest.getTenantId() != null && entity.getPartitionId() != null) {
PartitionEntity partitionEntity = myPartitionLookupSvc.getPartitionByName(theRequest.getTenantId());
//partitionEntity should never be null
if (partitionEntity != null && !partitionEntity.getId().equals(entity.getPartitionId().getPartitionId())) {

View File

@ -57,7 +57,7 @@ import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProviderFactory;
import ca.uhn.fhir.jpa.search.cache.SearchCacheStatusEnum;

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao.expunge;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.jpa.entity.Batch2JobInstanceEntity;
import ca.uhn.fhir.jpa.entity.Batch2WorkChunkEntity;
import ca.uhn.fhir.jpa.entity.BulkImportJobEntity;
@ -77,12 +78,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.transaction.annotation.Propagation;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
@ -98,22 +96,15 @@ public class ExpungeEverythingService implements IExpungeEverythingService {
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
protected EntityManager myEntityManager;
@Autowired
private PlatformTransactionManager myPlatformTransactionManager;
private HapiTransactionService myTxService;
@Autowired
protected IInterceptorBroadcaster myInterceptorBroadcaster;
private TransactionTemplate myTxTemplate;
@Autowired
private MemoryCacheService myMemoryCacheService;
private int deletedResourceEntityCount;
@PostConstruct
public void initTxTemplate() {
myTxTemplate = new TransactionTemplate(myPlatformTransactionManager);
}
@Override
public void expungeEverything(@Nullable RequestDetails theRequest) {
@ -127,68 +118,64 @@ public class ExpungeEverythingService implements IExpungeEverythingService {
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESTORAGE_EXPUNGE_EVERYTHING, hooks);
ourLog.info("BEGINNING GLOBAL $expunge");
myTxTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
myTxTemplate.execute(t -> {
myTxService.withRequest(theRequest).withPropagation(Propagation.REQUIRES_NEW).execute(()-> {
counter.addAndGet(doExpungeEverythingQuery("UPDATE " + TermCodeSystem.class.getSimpleName() + " d SET d.myCurrentVersion = null"));
return null;
});
counter.addAndGet(expungeEverythingByTypeWithoutPurging(Batch2WorkChunkEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(Batch2JobInstanceEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(NpmPackageVersionResourceEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(NpmPackageVersionEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(NpmPackageEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(SearchParamPresentEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(BulkImportJobFileEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(BulkImportJobEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ForcedId.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamDate.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamNumber.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamQuantity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamQuantityNormalized.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamString.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamToken.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamUri.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamCoords.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedComboStringUnique.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedComboTokenNonUnique.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceLink.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(SearchResult.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(SearchInclude.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermValueSetConceptDesignation.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermValueSetConcept.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermValueSet.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptParentChildLink.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptMapGroupElementTarget.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptMapGroupElement.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptMapGroup.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptMap.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptProperty.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptDesignation.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConcept.class));
myTxTemplate.execute(t -> {
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, Batch2WorkChunkEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, Batch2JobInstanceEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, NpmPackageVersionResourceEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, NpmPackageVersionEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, NpmPackageEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, SearchParamPresentEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, BulkImportJobFileEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, BulkImportJobEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ForcedId.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceIndexedSearchParamDate.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceIndexedSearchParamNumber.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceIndexedSearchParamQuantity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceIndexedSearchParamQuantityNormalized.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceIndexedSearchParamString.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceIndexedSearchParamToken.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceIndexedSearchParamUri.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceIndexedSearchParamCoords.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceIndexedComboStringUnique.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceIndexedComboTokenNonUnique.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceLink.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, SearchResult.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, SearchInclude.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TermValueSetConceptDesignation.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TermValueSetConcept.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TermValueSet.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TermConceptParentChildLink.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TermConceptMapGroupElementTarget.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TermConceptMapGroupElement.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TermConceptMapGroup.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TermConceptMap.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TermConceptProperty.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TermConceptDesignation.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TermConcept.class));
myTxService.withRequest(theRequest).withPropagation(Propagation.REQUIRES_NEW).execute(()-> {
for (TermCodeSystem next : myEntityManager.createQuery("SELECT c FROM " + TermCodeSystem.class.getName() + " c", TermCodeSystem.class).getResultList()) {
next.setCurrentVersion(null);
myEntityManager.merge(next);
}
return null;
});
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermCodeSystemVersion.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermCodeSystem.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(SubscriptionTable.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceHistoryTag.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceTag.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(TagDefinition.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceHistoryProvenanceEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceHistoryTable.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TermCodeSystemVersion.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TermCodeSystem.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, SubscriptionTable.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceHistoryTag.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceTag.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, TagDefinition.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceHistoryProvenanceEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceHistoryTable.class));
int counterBefore = counter.get();
counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceTable.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(PartitionEntity.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceTable.class));
counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, PartitionEntity.class));
deletedResourceEntityCount = counter.get() - counterBefore;
myTxTemplate.execute(t -> {
myTxService.withRequest(theRequest).withPropagation(Propagation.REQUIRES_NEW).execute(()-> {
counter.addAndGet(doExpungeEverythingQuery("DELETE from " + Search.class.getSimpleName() + " d"));
return null;
});
purgeAllCaches();
@ -202,19 +189,15 @@ public class ExpungeEverythingService implements IExpungeEverythingService {
}
private void purgeAllCaches() {
myTxTemplate.execute(t -> {
myMemoryCacheService.invalidateAllCaches();
return null;
});
}
private int expungeEverythingByTypeWithoutPurging(Class<?> theEntityType) {
private int expungeEverythingByTypeWithoutPurging(RequestDetails theRequest, Class<?> theEntityType) {
int outcome = 0;
while (true) {
StopWatch sw = new StopWatch();
@SuppressWarnings("ConstantConditions")
int count = myTxTemplate.execute(t -> {
int count = myTxService.withRequest(theRequest).withPropagation(Propagation.REQUIRES_NEW).execute(()-> {
CriteriaBuilder cb = myEntityManager.getCriteriaBuilder();
CriteriaQuery<?> cq = cb.createQuery(theEntityType);
cq.from(theEntityType);
@ -239,7 +222,7 @@ public class ExpungeEverythingService implements IExpungeEverythingService {
@Override
public int expungeEverythingByType(Class<?> theEntityType) {
int result = expungeEverythingByTypeWithoutPurging(theEntityType);
int result = expungeEverythingByTypeWithoutPurging(null, theEntityType);
purgeAllCaches();
return result;
}

View File

@ -2,7 +2,7 @@ package ca.uhn.fhir.jpa.dao.mdm;
/*-
* #%L
* HAPI FHIR JPA Server - Master Data Management
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%

View File

@ -28,6 +28,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.api.model.DeleteConflict;
import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService;
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -42,42 +43,32 @@ import org.slf4j.LoggerFactory;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.transaction.annotation.Propagation;
import java.util.Collections;
import java.util.List;
/**
* Used by {@link CascadingDeleteInterceptor} to handle {@link DeleteConflictList}s in a thead-safe way.
*
* <p>
* Specifically, this class spawns an inner transaction for each {@link DeleteConflictList}. This class is meant to handle any potential delete collisions (ex {@link ResourceGoneException} or {@link ResourceVersionConflictException}. In the former case, we swallow the Exception in the inner transaction then continue. In the latter case, we retry according to the RETRY_BACKOFF_PERIOD and RETRY_MAX_ATTEMPTS before giving up.
*/
public class ThreadSafeResourceDeleterSvc {
private static final String REQ_DET_KEY_IN_NEW_TRANSACTION = ThreadSafeResourceDeleterSvc.class.getName() + "REQ_DET_KEY_IN_NEW_TRANSACTION";
public static final long RETRY_BACKOFF_PERIOD = 100L;
public static final int RETRY_MAX_ATTEMPTS = 4;
private static final String REQ_DET_KEY_IN_NEW_TRANSACTION = ThreadSafeResourceDeleterSvc.class.getName() + "REQ_DET_KEY_IN_NEW_TRANSACTION";
private static final Logger ourLog = LoggerFactory.getLogger(ThreadSafeResourceDeleterSvc.class);
private final DaoRegistry myDaoRegistry;
private final IInterceptorBroadcaster myInterceptorBroadcaster;
private final TransactionTemplate myTxTemplateRequired;
private final TransactionTemplate myTxTemplateRequiresNew;
private final RetryTemplate myRetryTemplate = getRetryTemplate();
private final IHapiTransactionService myTransactionService;
public ThreadSafeResourceDeleterSvc(DaoRegistry theDaoRegistry, IInterceptorBroadcaster theInterceptorBroadcaster, PlatformTransactionManager thePlatformTransactionManager) {
public ThreadSafeResourceDeleterSvc(DaoRegistry theDaoRegistry, IInterceptorBroadcaster theInterceptorBroadcaster, IHapiTransactionService theTransactionService) {
myDaoRegistry = theDaoRegistry;
myInterceptorBroadcaster = theInterceptorBroadcaster;
myTxTemplateRequired = new TransactionTemplate(thePlatformTransactionManager);
myTxTemplateRequired.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
myTxTemplateRequiresNew = new TransactionTemplate(thePlatformTransactionManager);
myTxTemplateRequiresNew.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
myTransactionService = theTransactionService;
}
/**
@ -121,13 +112,20 @@ public class ThreadSafeResourceDeleterSvc {
// Avoid nesting multiple new transactions deep. This can easily cause
// thread pools to get exhausted.
Propagation propagation;
if (theRequest == null || previousNewTransactionValue != null) {
myTxTemplateRequired.execute(s -> doDelete(theRequest, theConflictList, theTransactionDetails, nextSource, dao));
propagation = Propagation.REQUIRED;
} else {
theRequest.getUserData().put(REQ_DET_KEY_IN_NEW_TRANSACTION, REQ_DET_KEY_IN_NEW_TRANSACTION);
myTxTemplateRequiresNew.execute(s -> doDelete(theRequest, theConflictList, theTransactionDetails, nextSource, dao));
propagation = Propagation.REQUIRES_NEW;
}
myTransactionService
.withRequest(theRequest)
.withTransactionDetails(theTransactionDetails)
.withPropagation(propagation)
.execute(() -> doDelete(theRequest, theConflictList, theTransactionDetails, nextSource, dao));
return 1;
} catch (ResourceGoneException exception) {
ourLog.info("{} is already deleted. Skipping cascade delete of this resource", nextSourceId);

View File

@ -40,7 +40,6 @@ import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import java.io.Serializable;
import java.util.ArrayList;

View File

@ -23,10 +23,8 @@ package ca.uhn.fhir.jpa.interceptor;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.apache.commons.lang3.Validate;
/**

View File

@ -2,7 +2,7 @@ package ca.uhn.fhir.jpa.model.cross;
/*-
* #%L
* HAPI FHIR JPA Model
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%

View File

@ -40,7 +40,7 @@ import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;

View File

@ -35,7 +35,7 @@ import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistryController;
import ca.uhn.fhir.jpa.searchparam.util.SearchParameterHelper;
@ -49,14 +49,12 @@ import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.SearchParameterUtil;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
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.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.utilities.json.model.JsonElement;
import org.hl7.fhir.utilities.json.model.JsonObject;
import org.hl7.fhir.utilities.npm.IPackageCacheManager;
import org.hl7.fhir.utilities.npm.NpmPackage;
@ -72,9 +70,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static org.apache.commons.lang3.StringUtils.defaultString;

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.partition;
*/
import ca.uhn.fhir.jpa.entity.PartitionEntity;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import javax.annotation.Nullable;
@ -46,7 +47,7 @@ public interface IPartitionLookupSvc {
void clearCaches();
PartitionEntity createPartition(PartitionEntity thePartition);
PartitionEntity createPartition(PartitionEntity thePartition, RequestDetails theRequestDetails);
PartitionEntity updatePartition(PartitionEntity thePartition);

View File

@ -22,16 +22,23 @@ package ca.uhn.fhir.jpa.partition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.data.IPartitionDao;
import ca.uhn.fhir.jpa.entity.PartitionEntity;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.sl.cache.CacheFactory;
import ca.uhn.fhir.sl.cache.CacheLoader;
import ca.uhn.fhir.sl.cache.LoadingCache;
import ca.uhn.fhir.util.ICallable;
import org.apache.commons.lang3.Validate;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -42,6 +49,7 @@ import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Optional;
@ -58,15 +66,17 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
@Autowired
private PartitionSettings myPartitionSettings;
@Autowired
private PlatformTransactionManager myTxManager;
private IInterceptorService myInterceptorService;
@Autowired
private IPartitionDao myPartitionDao;
private LoadingCache<String, PartitionEntity> myNameToPartitionCache;
private LoadingCache<Integer, PartitionEntity> myIdToPartitionCache;
private TransactionTemplate myTxTemplate;
@Autowired
private FhirContext myFhirCtx;
@Autowired
private PlatformTransactionManager myTxManager;
private TransactionTemplate myTxTemplate;
/**
* Constructor
@ -113,15 +123,25 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
@Override
@Transactional
public PartitionEntity createPartition(PartitionEntity thePartition) {
public PartitionEntity createPartition(PartitionEntity thePartition, RequestDetails theRequestDetails) {
validateNotInUnnamedPartitionMode();
validateHaveValidPartitionIdAndName(thePartition);
validatePartitionNameDoesntAlreadyExist(thePartition.getName());
ourLog.info("Creating new partition with ID {} and Name {}", thePartition.getId(), thePartition.getName());
myPartitionDao.save(thePartition);
return thePartition;
PartitionEntity retVal = myPartitionDao.save(thePartition);
// Interceptor call: STORAGE_PARTITION_CREATED
if (myInterceptorService.hasHooks(Pointcut.STORAGE_PARTITION_CREATED)) {
HookParams params = new HookParams()
.add(RequestPartitionId.class, thePartition.toRequestPartitionId())
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
myInterceptorService.callHooks(Pointcut.STORAGE_PARTITION_CREATED, params);
}
return retVal;
}
@Override
@ -167,7 +187,7 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
@Override
public List<PartitionEntity> listPartitions() {
List<PartitionEntity> allPartitions = myPartitionDao.findAll();
List<PartitionEntity> allPartitions = myPartitionDao.findAll();
return allPartitions;
}
@ -202,16 +222,31 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
}
}
private PartitionEntity lookupPartitionByName(@Nonnull String theName) {
return executeInTransaction(() -> myPartitionDao.findForName(theName))
.orElseThrow(() -> {
String msg = myFhirCtx.getLocalizer().getMessageSanitized(PartitionLookupSvcImpl.class, "invalidName", theName);
return new ResourceNotFoundException(msg);
});
}
private PartitionEntity lookupPartitionById(@Nonnull Integer theId) {
return executeInTransaction(() -> myPartitionDao.findById(theId))
.orElseThrow(() -> {
String msg = myFhirCtx.getLocalizer().getMessageSanitized(PartitionLookupSvcImpl.class, "unknownPartitionId", theId);
return new ResourceNotFoundException(msg);
});
}
protected <T> T executeInTransaction(ICallable<T> theCallable) {
return myTxTemplate.execute(tx -> theCallable.call());
}
private class NameToPartitionCacheLoader implements @NonNull CacheLoader<String, PartitionEntity> {
@Nullable
@Override
public PartitionEntity load(@NonNull String theName) {
return myTxTemplate.execute(t -> myPartitionDao
.findForName(theName)
.orElseThrow(() -> {
String msg = myFhirCtx.getLocalizer().getMessageSanitized(PartitionLookupSvcImpl.class, "invalidName", theName);
return new ResourceNotFoundException(msg);
}));
return lookupPartitionByName(theName);
}
}
@ -219,12 +254,7 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
@Nullable
@Override
public PartitionEntity load(@NonNull Integer theId) {
return myTxTemplate.execute(t -> myPartitionDao
.findById(theId)
.orElseThrow(() -> {
String msg = myFhirCtx.getLocalizer().getMessageSanitized(PartitionLookupSvcImpl.class, "unknownPartitionId", theId);
return new ResourceNotFoundException(msg);
}));
return lookupPartitionById(theId);
}
}

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.jpa.entity.PartitionEntity;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import ca.uhn.fhir.util.ParametersUtil;
import org.hl7.fhir.instance.model.api.IBase;
@ -35,7 +35,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import static ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.validatePartitionIdSupplied;
@ -70,14 +69,15 @@ public class PartitionManagementProvider {
@ResourceParam IBaseParameters theRequest,
@OperationParam(name = ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, min = 1, max = 1, typeName = "integer") IPrimitiveType<Integer> thePartitionId,
@OperationParam(name = ProviderConstants.PARTITION_MANAGEMENT_PARTITION_NAME, min = 1, max = 1, typeName = "code") IPrimitiveType<String> thePartitionName,
@OperationParam(name = ProviderConstants.PARTITION_MANAGEMENT_PARTITION_DESC, min = 0, max = 1, typeName = "string") IPrimitiveType<String> thePartitionDescription
@OperationParam(name = ProviderConstants.PARTITION_MANAGEMENT_PARTITION_DESC, min = 0, max = 1, typeName = "string") IPrimitiveType<String> thePartitionDescription,
RequestDetails theRequestDetails
) {
validatePartitionIdSupplied(myCtx, toValueOrNull(thePartitionId));
PartitionEntity input = parseInput(thePartitionId, thePartitionName, thePartitionDescription);
// Note: Input validation happens inside IPartitionLookupSvc
PartitionEntity output = myPartitionLookupSvc.createPartition(input);
PartitionEntity output = myPartitionLookupSvc.createPartition(input, theRequestDetails);
IBaseParameters retVal = prepareOutput(output);

View File

@ -35,7 +35,7 @@ import java.util.Objects;
public class RequestPartitionHelperSvc extends BaseRequestPartitionHelperSvc {
@Autowired
private IPartitionLookupSvc myPartitionConfigSvc;
IPartitionLookupSvc myPartitionConfigSvc;
@Override
protected RequestPartitionId validateAndNormalizePartitionIds(RequestPartitionId theRequestPartitionId) {

View File

@ -32,7 +32,7 @@ import ca.uhn.fhir.jpa.api.pid.MixedResourcePidList;
import ca.uhn.fhir.jpa.api.svc.IBatch2DaoSvc;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.Constants;

View File

@ -34,6 +34,7 @@ import ca.uhn.fhir.jpa.dao.HistoryBuilderFactory;
import ca.uhn.fhir.jpa.dao.IJpaStorageResourceParser;
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
@ -59,11 +60,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nonnull;
import javax.persistence.EntityManager;
@ -82,9 +78,9 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
/*
* Autowired fields
*/
private final RequestDetails myRequest;
protected final RequestDetails myRequest;
@Autowired
protected PlatformTransactionManager myTxManager;
protected HapiTransactionService myTxService;
@PersistenceContext
private EntityManager myEntityManager;
@Autowired
@ -213,10 +209,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
final ISearchBuilder sb = mySearchBuilderFactory.newSearchBuilder(dao, resourceName, resourceType);
final List<JpaPid> pidsSubList = mySearchCoordinatorSvc.getResources(myUuid, theFromIndex, theToIndex, myRequest);
TransactionTemplate template = new TransactionTemplate(myTxManager);
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
return template.execute(theStatus -> toResourceList(sb, pidsSubList));
return myTxService.withRequest(myRequest).execute(() -> toResourceList(sb, pidsSubList));
}
@ -225,7 +218,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
*/
public boolean ensureSearchEntityLoaded() {
if (mySearchEntity == null) {
Optional<Search> searchOpt = mySearchCacheSvc.fetchByUuid(myUuid);
Optional<Search> searchOpt = myTxService.withRequest(myRequest).execute(() -> mySearchCacheSvc.fetchByUuid(myUuid));
if (!searchOpt.isPresent()) {
return false;
}
@ -262,7 +255,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
key = MemoryCacheService.HistoryCountKey.forSystem();
}
Function<MemoryCacheService.HistoryCountKey, Integer> supplier = k -> new TransactionTemplate(myTxManager).execute(t -> {
Function<MemoryCacheService.HistoryCountKey, Integer> supplier = k -> myTxService.withRequest(myRequest).execute(() -> {
HistoryBuilder historyBuilder = myHistoryBuilderFactory.newHistoryBuilder(mySearchEntity.getResourceType(), mySearchEntity.getResourceId(), mySearchEntity.getLastUpdatedLow(), mySearchEntity.getLastUpdatedHigh());
Long count = historyBuilder.fetchCount(getRequestPartitionIdForHistory());
return count.intValue();
@ -299,14 +292,9 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
@Nonnull
@Override
public List<IBaseResource> getResources(final int theFromIndex, final int theToIndex) {
TransactionTemplate template = new TransactionTemplate(myTxManager);
template.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) {
boolean entityLoaded = ensureSearchEntityLoaded();
assert entityLoaded;
}
myTxService.withRequest(myRequest).execute(() -> {
boolean entityLoaded = ensureSearchEntityLoaded();
assert entityLoaded;
});
assert mySearchEntity != null;
@ -314,7 +302,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
switch (mySearchEntity.getSearchType()) {
case HISTORY:
return template.execute(theStatus -> doHistoryInTransaction(mySearchEntity.getOffset(), theFromIndex, theToIndex));
return myTxService.withRequest(myRequest).execute(() -> doHistoryInTransaction(mySearchEntity.getOffset(), theFromIndex, theToIndex));
case SEARCH:
case EVERYTHING:
default:
@ -365,8 +353,8 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
}
@VisibleForTesting
public void setTxManagerForUnitTest(PlatformTransactionManager theTxManager) {
myTxManager = theTxManager;
public void setTxServiceForUnitTest(HapiTransactionService theTxManager) {
myTxService = theTxManager;
}
// Note: Leave as protected, HSPC depends on this
@ -388,7 +376,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
if (mySearchEntity.getSearchType() == SearchTypeEnum.HISTORY) {
return null;
} else {
return mySearchCoordinatorSvc.getSearchTotal(myUuid).orElse(null);
return mySearchCoordinatorSvc.getSearchTotal(myUuid, myRequest).orElse(null);
}
}

View File

@ -34,8 +34,6 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nonnull;
import java.util.List;
@ -70,9 +68,7 @@ public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundl
final List<JpaPid> pids = mySearchTask.getResourcePids(theFromIndex, theToIndex);
ourLog.trace("Done fetching search resource PIDs");
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
List<IBaseResource> retVal = txTemplate.execute(theStatus -> toResourceList(mySearchBuilder, pids));
List<IBaseResource> retVal = myTxService.withRequest(myRequest).execute(() -> toResourceList(mySearchBuilder, pids));
long totalCountWanted = theToIndex - theFromIndex;
long totalCountMatch = (int) retVal

View File

@ -36,6 +36,7 @@ import ca.uhn.fhir.jpa.dao.BaseStorageDao;
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.jpa.dao.search.ResourceNotFoundInIndexException;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
@ -62,6 +63,7 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
import ca.uhn.fhir.util.AsyncUtil;
import ca.uhn.fhir.util.ICallable;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.util.UrlUtil;
import com.google.common.annotations.VisibleForTesting;
@ -72,11 +74,7 @@ import org.springframework.data.domain.AbstractPageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -86,6 +84,7 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@ -103,7 +102,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
private final FhirContext myContext;
private final DaoConfig myDaoConfig;
private final IInterceptorBroadcaster myInterceptorBroadcaster;
private final PlatformTransactionManager myManagedTxManager;
private final HapiTransactionService myTxService;
private final ISearchCacheSvc mySearchCacheSvc;
private final ISearchResultCacheSvc mySearchResultCacheSvc;
private final DaoRegistry myDaoRegistry;
@ -115,18 +114,15 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
private final SearchStrategyFactory mySearchStrategyFactory;
private final ExceptionService myExceptionSvc;
private final BeanFactory myBeanFactory;
private Integer myLoadingThrottleForUnitTests = null;
private long myMaxMillisToWaitForRemoteResults = DateUtils.MILLIS_PER_MINUTE;
private boolean myNeverUseLocalSearchForUnitTests;
private int mySyncSize = DEFAULT_SYNC_SIZE;
private final ConcurrentHashMap<String, SearchTask> myIdToSearchTask = new ConcurrentHashMap<>();
private final Consumer<String> myOnRemoveSearchTask = (theId) -> myIdToSearchTask.remove(theId);
private final StorageInterceptorHooksFacade myStorageInterceptorHooks;
private Integer myLoadingThrottleForUnitTests = null;
private long myMaxMillisToWaitForRemoteResults = DateUtils.MILLIS_PER_MINUTE;
private boolean myNeverUseLocalSearchForUnitTests;
private int mySyncSize = DEFAULT_SYNC_SIZE;
/**
* Constructor
@ -135,7 +131,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
FhirContext theContext,
DaoConfig theDaoConfig,
IInterceptorBroadcaster theInterceptorBroadcaster,
PlatformTransactionManager theManagedTxManager,
HapiTransactionService theTxService,
ISearchCacheSvc theSearchCacheSvc,
ISearchResultCacheSvc theSearchResultCacheSvc,
DaoRegistry theDaoRegistry,
@ -152,7 +148,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
myContext = theContext;
myDaoConfig = theDaoConfig;
myInterceptorBroadcaster = theInterceptorBroadcaster;
myManagedTxManager = theManagedTxManager;
myTxService = theTxService;
mySearchCacheSvc = theSearchCacheSvc;
mySearchResultCacheSvc = theSearchResultCacheSvc;
myDaoRegistry = theDaoRegistry;
@ -208,10 +204,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
* fetch resources.
*/
@Override
@Transactional(propagation = Propagation.NEVER)
public List<JpaPid> getResources(final String theUuid, int theFrom, int theTo, @Nullable RequestDetails theRequestDetails) {
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
assert !TransactionSynchronizationManager.isActualTransactionActive();
// If we're actively searching right now, don't try to do anything until at least one batch has been
// persisted in the DB
@ -244,9 +238,14 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
}
}
search = mySearchCacheSvc
Callable<Search> searchCallback = () -> mySearchCacheSvc
.fetchByUuid(theUuid)
.orElseThrow(() -> myExceptionSvc.newUnknownSearchException(theUuid));
if (theRequestDetails != null) {
search = myTxService.withRequest(theRequestDetails).execute(searchCallback);
} else {
search = HapiTransactionService.invokeCallableAndHandleAnyException(searchCallback);
}
QueryParameterUtils.verifySearchHasntFailedOrThrowInternalErrorException(search);
if (search.getStatus() == SearchStatusEnum.FINISHED) {
@ -299,16 +298,22 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
ourLog.trace("Finished looping");
List<JpaPid> pids = mySearchResultCacheSvc.fetchResultPids(search, theFrom, theTo);
if (pids == null) {
throw myExceptionSvc.newUnknownSearchException(theUuid);
}
List<JpaPid> pids = fetchResultPids(theUuid, theFrom, theTo, theRequestDetails, search);
ourLog.trace("Fetched {} results", pids.size());
return pids;
}
@Nonnull
private List<JpaPid> fetchResultPids(String theUuid, int theFrom, int theTo, @Nullable RequestDetails theRequestDetails, Search theSearch) {
List<JpaPid> pids = myTxService.withRequest(theRequestDetails).execute(() -> mySearchResultCacheSvc.fetchResultPids(theSearch, theFrom, theTo));
if (pids == null) {
throw myExceptionSvc.newUnknownSearchException(theUuid);
}
return pids;
}
@Override
public IBundleProvider registerSearch(final IFhirResourceDao<?> theCallingDao, final SearchParameterMap theParams, String theResourceType, CacheControlDirective theCacheControlDirective, RequestDetails theRequestDetails, RequestPartitionId theRequestPartitionId) {
final String searchUuid = UUID.randomUUID().toString();
@ -337,7 +342,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
if (theParams.isLoadSynchronous() || loadSynchronousUpTo != null || isOffsetQuery) {
if (mySearchStrategyFactory.isSupportsHSearchDirect(theResourceType, theParams, theRequestDetails)) {
ourLog.info("Search {} is using direct load strategy", searchUuid);
SearchStrategyFactory.ISearchStrategy direct = mySearchStrategyFactory.makeDirectStrategy(searchUuid, theResourceType, theParams, theRequestDetails);
SearchStrategyFactory.ISearchStrategy direct = mySearchStrategyFactory.makeDirectStrategy(searchUuid, theResourceType, theParams, theRequestDetails);
try {
return direct.get();
@ -429,7 +434,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
}
@Override
public Optional<Integer> getSearchTotal(String theUuid) {
public Optional<Integer> getSearchTotal(String theUuid, @Nullable RequestDetails theRequestDetails) {
SearchTask task = myIdToSearchTask.get(theUuid);
if (task != null) {
return Optional.ofNullable(task.awaitInitialSync());
@ -439,9 +444,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
* In case there is no running search, if the total is listed as accurate we know one is coming
* so let's wait a bit for it to show up
*/
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
Optional<Search> search = mySearchCacheSvc.fetchByUuid(theUuid);
Optional<Search> search = myTxService.withRequest(theRequestDetails).execute(()->mySearchCacheSvc.fetchByUuid(theUuid));
if (search.isPresent()) {
Optional<SearchParameterMap> searchParameterMap = search.get().getSearchParameterMap();
if (searchParameterMap.isPresent() && searchParameterMap.get().getSearchTotalMode() == SearchTotalModeEnum.ACCURATE) {
@ -487,10 +490,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
@Nullable
private PersistedJpaBundleProvider findCachedQuery(SearchParameterMap theParams, String theResourceType, RequestDetails theRequestDetails, String theQueryString, RequestPartitionId theRequestPartitionId) {
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
// May be null
return txTemplate.execute(t -> {
return myTxService.withRequest(theRequestDetails).execute(() -> {
// Interceptor call: STORAGE_PRECHECK_FOR_CACHED_SEARCH
HookParams params = new HookParams()

View File

@ -32,6 +32,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IResultIterator;
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
@ -48,9 +49,6 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import javax.persistence.EntityManager;
import java.io.IOException;
@ -79,7 +77,7 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc {
private DaoRegistry myDaoRegistry;
@Autowired
private PlatformTransactionManager myManagedTxManager;
private HapiTransactionService myTxService;
@Autowired
private IInterceptorBroadcaster myInterceptorBroadcaster;
@ -89,6 +87,7 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc {
private int mySyncSize = 250;
@Override
public IBundleProvider executeQuery(SearchParameterMap theParams, RequestDetails theRequestDetails, String theSearchUuid, ISearchBuilder theSb, Integer theLoadSynchronousUpTo, RequestPartitionId theRequestPartitionId) {
SearchRuntimeDetails searchRuntimeDetails = new SearchRuntimeDetails(theRequestDetails, theSearchUuid);
searchRuntimeDetails.setLoadSynchronous(true);
@ -98,10 +97,8 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc {
boolean wantCount = theParamWantOnlyCount || theParamOrConfigWantCount;
// Execute the query and make sure we return distinct results
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
txTemplate.setReadOnly(theParams.isLoadSynchronous() || theParams.isOffsetQuery());
return txTemplate.execute(t -> {
return myTxService.withRequest(theRequestDetails).readOnly().execute(() -> {
// Load the results synchronously
final List<JpaPid> pids = new ArrayList<>();

View File

@ -25,14 +25,14 @@ import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
import ca.uhn.fhir.jpa.search.ExceptionService;
import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc;
import ca.uhn.fhir.jpa.search.cache.ISearchResultCacheSvc;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.List;
@ -41,10 +41,11 @@ public class SearchContinuationTask extends SearchTask {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchContinuationTask.class);
private final ExceptionService myExceptionSvc;
private final RequestDetails myRequestDetails;
public SearchContinuationTask(
SearchTaskParameters theCreationParams,
PlatformTransactionManager theManagedTxManager,
HapiTransactionService theTxService,
FhirContext theContext,
IInterceptorBroadcaster theInterceptorBroadcaster,
SearchBuilderFactory theSearchBuilderFactory,
@ -56,7 +57,7 @@ public class SearchContinuationTask extends SearchTask {
) {
super(
theCreationParams,
theManagedTxManager,
theTxService,
theContext,
theInterceptorBroadcaster,
theSearchBuilderFactory,
@ -66,15 +67,14 @@ public class SearchContinuationTask extends SearchTask {
thePagingProvider
);
myRequestDetails = theCreationParams.Request;
myExceptionSvc = theExceptionSvc;
}
@Override
public Void call() {
try {
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.afterPropertiesSet();
txTemplate.execute(t -> {
myTxService.withRequest(myRequestDetails).execute(() -> {
List<JpaPid> previouslyAddedResourcePids = mySearchResultCacheSvc.fetchAllResultPids(getSearch());
if (previouslyAddedResourcePids == null) {
throw myExceptionSvc.newUnknownSearchException(getSearch().getUuid());
@ -82,8 +82,8 @@ public class SearchContinuationTask extends SearchTask {
ourLog.trace("Have {} previously added IDs in search: {}", previouslyAddedResourcePids.size(), getSearch().getUuid());
setPreviouslyAddedResourcePids(previouslyAddedResourcePids);
return null;
});
} catch (Throwable e) {
ourLog.error("Failure processing search", e);
getSearch().setFailureMessage(e.getMessage());

View File

@ -31,6 +31,7 @@ import ca.uhn.fhir.jpa.api.dao.IDao;
import ca.uhn.fhir.jpa.dao.IResultIterator;
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
@ -57,14 +58,8 @@ import co.elastic.apm.api.Transaction;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.orm.jpa.JpaDialect;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import javax.annotation.Nonnull;
import java.io.IOException;
@ -123,10 +118,8 @@ public class SearchTask implements Callable<Void> {
private final int mySyncSize;
private final Integer myLoadingThrottleForUnitTests;
private boolean myCustomIsolationSupported;
// injected beans
protected final PlatformTransactionManager myManagedTxManager;
protected final HapiTransactionService myTxService;
protected final FhirContext myContext;
private final IInterceptorBroadcaster myInterceptorBroadcaster;
private final SearchBuilderFactory<JpaPid> mySearchBuilderFactory;
@ -140,7 +133,7 @@ public class SearchTask implements Callable<Void> {
*/
public SearchTask(
SearchTaskParameters theCreationParams,
PlatformTransactionManager theManagedTxManager,
HapiTransactionService theManagedTxManager,
FhirContext theContext,
IInterceptorBroadcaster theInterceptorBroadcaster,
SearchBuilderFactory theSearchBuilderFactory,
@ -150,7 +143,7 @@ public class SearchTask implements Callable<Void> {
IPagingProvider thePagingProvider
) {
// beans
myManagedTxManager = theManagedTxManager;
myTxService = theManagedTxManager;
myContext = theContext;
myInterceptorBroadcaster = theInterceptorBroadcaster;
mySearchBuilderFactory = theSearchBuilderFactory;
@ -174,17 +167,6 @@ public class SearchTask implements Callable<Void> {
mySearchRuntimeDetails.setQueryString(myParams.toNormalizedQueryString(myCallingDao.getContext()));
myRequestPartitionId = theCreationParams.RequestPartitionId;
myParentTransaction = ElasticApm.currentTransaction();
if (myManagedTxManager instanceof JpaTransactionManager) {
JpaDialect jpaDialect = ((JpaTransactionManager) myManagedTxManager).getJpaDialect();
if (jpaDialect instanceof HibernateJpaDialect) {
myCustomIsolationSupported = true;
}
}
if (!myCustomIsolationSupported) {
ourLog.warn("JPA dialect does not support transaction isolation! This can have an impact on search performance.");
}
}
/**
@ -285,23 +267,11 @@ public class SearchTask implements Callable<Void> {
}
public void saveSearch() {
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theArg0) {
doSaveSearch();
}
});
myTxService.execute(myRequest, null, Propagation.REQUIRES_NEW, Isolation.DEFAULT, ()->doSaveSearch());
}
private void saveUnsynced(final IResultIterator theResultIter) {
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theArg0) {
myTxService.withRequest(myRequest).execute(()->{
if (mySearch.getId() == null) {
doSaveSearch();
}
@ -380,8 +350,7 @@ public class SearchTask implements Callable<Void> {
doSaveSearch();
ourLog.trace("saveUnsynced() - pre-commit");
}
});
});
ourLog.trace("saveUnsynced() - post-commit");
}
@ -418,19 +387,7 @@ public class SearchTask implements Callable<Void> {
// Create an initial search in the DB and give it an ID
saveSearch();
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
if (myCustomIsolationSupported) {
txTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
}
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) {
doSearch();
}
});
myTxService.execute(myRequest, null, Propagation.REQUIRED, Isolation.READ_COMMITTED, ()->doSearch());
mySearchRuntimeDetails.setSearchStatus(mySearch.getStatus());
if (mySearch.getStatus() == SearchStatusEnum.FINISHED) {
@ -549,16 +506,12 @@ public class SearchTask implements Callable<Void> {
ourLog.trace("Got count {}", count);
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theArg0) {
mySearch.setTotalCount(count.intValue());
if (myParamWantOnlyCount) {
mySearch.setStatus(SearchStatusEnum.FINISHED);
}
doSaveSearch();
myTxService.withRequest(myRequest).execute(()->{
mySearch.setTotalCount(count.intValue());
if (myParamWantOnlyCount) {
mySearch.setStatus(SearchStatusEnum.FINISHED);
}
doSaveSearch();
});
if (myParamWantOnlyCount) {
return;

View File

@ -15,7 +15,7 @@ import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.jpa.dao.mdm.MdmExpansionCacheSvc;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.dao.IMdmLinkDao;

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -23,7 +23,7 @@ package ca.uhn.fhir.cql.dstu3.provider;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.StringParam;

View File

@ -24,7 +24,7 @@ import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.StringParam;

View File

@ -2,7 +2,7 @@ package ca.uhn.fhir.cql.dstu3;
import ca.uhn.fhir.cql.BaseCqlDstu3Test;
import ca.uhn.fhir.cql.dstu3.provider.MeasureOperationsProvider;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.util.BundleUtil;
import org.hamcrest.Matchers;
import org.hl7.fhir.dstu3.model.Bundle;

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -21,7 +21,7 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.search.CompositeSearchParameterTestCases;
import ca.uhn.fhir.jpa.search.QuantitySearchParameterTestCases;
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;

View File

@ -17,7 +17,7 @@ import ca.uhn.fhir.jpa.entity.TermValueSet;
import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;

View File

@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.jpa.config.TestR4ConfigWithElasticHSearch;
import ca.uhn.fhir.jpa.test.BaseValueSetHSearchExpansionR4Test;
import ca.uhn.fhir.test.utilities.docker.RequiresDocker;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ -11,6 +12,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = TestR4ConfigWithElasticHSearch.class)
@RequiresDocker
public class ValueSetHSearchExpansionR4ElasticIT extends BaseValueSetHSearchExpansionR4Test {
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings;
import ca.uhn.fhir.jpa.subscription.channel.subscription.IChannelNamer;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader;

View File

@ -28,7 +28,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.pid.HomogeneousResourcePidList;
import ca.uhn.fhir.jpa.api.pid.IResourcePidList;
import ca.uhn.fhir.jpa.api.svc.IGoldenResourceSearchSvc;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.mdm.api.MdmConstants;

View File

@ -26,7 +26,7 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.api.MdmConstants;

View File

@ -24,7 +24,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.mdm.svc.MdmSearchParamSvc;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.rest.api.server.IBundleProvider;

View File

@ -21,7 +21,7 @@ import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig;

View File

@ -9,7 +9,7 @@ import ca.uhn.fhir.jpa.mdm.helper.testmodels.MDMLinkResults;
import ca.uhn.fhir.jpa.mdm.helper.testmodels.MDMState;
import ca.uhn.fhir.jpa.mdm.helper.testmodels.MdmTestLinkExpression;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;

View File

@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.mdm.provider;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
@ -18,23 +17,18 @@ import org.hl7.fhir.r4.model.StringType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.Servlet;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;

View File

@ -49,7 +49,7 @@ public class MdmProviderCreateLinkR4Test extends BaseLinkR4Test {
@Test
public void testCreateLinkWithMatchResultOnSamePartition() {
myPartitionSettings.setPartitioningEnabled(true);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1), null);
assertLinkCount(1);
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1);
@ -73,8 +73,8 @@ public class MdmProviderCreateLinkR4Test extends BaseLinkR4Test {
@Test
public void testCreateLinkWithMatchResultOnDifferentPartitions() {
myPartitionSettings.setPartitioningEnabled(true);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1), null);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2), null);
assertLinkCount(1);
RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1);

View File

@ -1,6 +1,6 @@
package ca.uhn.fhir.jpa.mdm.provider;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.mdm.api.MdmConstants;
import com.google.common.collect.Ordering;
import org.hl7.fhir.instance.model.api.IBaseResource;

View File

@ -47,6 +47,7 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
myToGoldenPatientId = new StringType(myToGoldenPatient.getIdElement().getValue());
}
@Override
@AfterEach
public void after() throws IOException {
myPartitionSettings.setPartitioningEnabled(new PartitionSettings().isPartitioningEnabled());
@ -112,7 +113,7 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
@Test
public void testMergeOnSamePartition() {
myPartitionSettings.setPartitioningEnabled(true);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1), null);
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1);
Patient fromGoldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId);
StringType fromGoldenPatientId = new StringType(fromGoldenPatient.getIdElement().getValue());
@ -147,9 +148,9 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
@Test
public void testMergeOnDifferentPartitions() {
myPartitionSettings.setPartitioningEnabled(true);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1), null);
RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2), null);
RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2);
Patient fromGoldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId1);
StringType fromGoldenPatientId = new StringType(fromGoldenPatient.getIdElement().getValue());

View File

@ -43,6 +43,7 @@ public class MdmProviderNotDuplicateGoldenResourceR4Test extends BaseProviderR4T
myTargetPatientId = new StringType(myTargetPatient.getIdElement().getValue());
}
@Override
@AfterEach
public void after() throws IOException {
myPartitionSettings.setPartitioningEnabled(false);
@ -86,7 +87,7 @@ public class MdmProviderNotDuplicateGoldenResourceR4Test extends BaseProviderR4T
@Test
public void testNotDuplicateGoldenResourceOnSamePartition() {
myPartitionSettings.setPartitioningEnabled(true);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1), null);
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1);
Patient goldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId);
StringType goldenPatientId = new StringType(goldenPatient.getIdElement().getValue());
@ -106,9 +107,9 @@ public class MdmProviderNotDuplicateGoldenResourceR4Test extends BaseProviderR4T
@Test
public void testNotDuplicateGoldenResourceOnDifferentPartitions() {
myPartitionSettings.setPartitioningEnabled(true);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1), null);
RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2), null);
RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2);
Patient goldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId1);
StringType goldenPatientId = new StringType(goldenPatient.getIdElement().getValue());

View File

@ -59,7 +59,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
@Test
public void testUpdateLinkMatchOnSamePartition() {
myPartitionSettings.setPartitioningEnabled(true);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1), null);
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1);
Patient patient = createPatientAndUpdateLinksOnPartition(buildFrankPatient(), requestPartitionId);
StringType patientId = new StringType(patient.getIdElement().getValue());
@ -84,8 +84,8 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
@Test
public void testUpdateLinkMatchOnDifferentPartitions() {
myPartitionSettings.setPartitioningEnabled(true);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1), null);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2), null);
RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1);
RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2);
Patient patient = createPatientOnPartition(buildFrankPatient(), true, false, requestPartitionId1);

View File

@ -3,7 +3,7 @@ package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
import ca.uhn.fhir.mdm.svc.MdmSubmitSvcImpl;
import ca.uhn.test.concurrency.PointcutLatch;

View File

@ -6,7 +6,7 @@ import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.entity.PartitionEntity;
import ca.uhn.fhir.jpa.mdm.provider.BaseLinkR4Test;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.test.Batch2JobHelper;
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
@ -65,8 +65,8 @@ public class MdmControllerSvcImplTest extends BaseLinkR4Test {
public void before() throws Exception {
super.before();
myPartitionSettings.setPartitioningEnabled(true);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1), null);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2), null);
myInterceptorService.registerInterceptor(myPartitionInterceptor);
myMdmSettings.setEnabled(true);
}

View File

@ -3,7 +3,7 @@ package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.mdm.api.IMdmLink;
import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;

View File

@ -7,9 +7,11 @@ import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.util.List;
import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.MATCH;
@ -22,6 +24,13 @@ class MdmLinkUpdaterSvcImplTest extends BaseMdmR4Test {
@Autowired
private IMdmLinkUpdaterSvc myMdmLinkUpdaterSvc;
@Override
@AfterEach
public void after() throws IOException {
super.after();
myMdmSettings.getMdmRules().setVersion("1");
}
@Test
public void testUpdateLinkNoMatch() {
// setup

View File

@ -4,7 +4,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.entity.PartitionEntity;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.r4.model.Patient;
@ -25,6 +25,7 @@ public class MdmResourceDaoSvcTest extends BaseMdmR4Test {
@Autowired
MdmResourceDaoSvc myResourceDaoSvc;
@Override
@AfterEach
public void after() throws IOException {
myPartitionSettings.setPartitioningEnabled(new PartitionSettings().isPartitioningEnabled());
@ -63,7 +64,7 @@ public class MdmResourceDaoSvcTest extends BaseMdmR4Test {
@Test
public void testSearchGoldenResourceOnSamePartition() {
myPartitionSettings.setPartitioningEnabled(true);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1), null);
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1);
Patient patientOnPartition = createPatientOnPartition(new Patient(), true, false, requestPartitionId);
Patient goodSourcePatient = addExternalEID(patientOnPartition, TEST_EID);
@ -79,9 +80,9 @@ public class MdmResourceDaoSvcTest extends BaseMdmR4Test {
@Test
public void testSearchGoldenResourceOnDifferentPartitions() {
myPartitionSettings.setPartitioningEnabled(true);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1), null);
RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1);
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2), null);
RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2);
Patient patientOnPartition = createPatientOnPartition(new Patient(), true, false, requestPartitionId1);
Patient goodSourcePatient = addExternalEID(patientOnPartition, TEST_EID);

View File

@ -6,7 +6,7 @@ import ca.uhn.fhir.batch2.model.JobInstance;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.jpa.model.dao;
/*-
* #%L
* HAPI FHIR JPA Model
* %%
* Copyright (C) 2014 - 2022 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%
*/
import ca.uhn.fhir.rest.api.server.storage.BaseResourcePersistentId;
import java.util.ArrayList;

View File

@ -0,0 +1,117 @@
package ca.uhn.fhir.jpa.model.dialect;
/*-
* #%L
* HAPI FHIR JPA Model
* %%
* Copyright (C) 2014 - 2022 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%
*/
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.util.ReflectionUtil;
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.SqlStringGenerationContext;
import org.hibernate.boot.registry.StandardServiceInitiator;
import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.enhanced.StandardOptimizerDescriptor;
import org.hibernate.service.Service;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.type.Type;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.Serializable;
import java.util.Map;
import java.util.Properties;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
/**
* 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.
*/
@SuppressWarnings("unused")
public class HapiSequenceStyleGenerator implements IdentifierGenerator, PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator {
private final SequenceStyleGenerator myGen = new SequenceStyleGenerator();
@Autowired
private ModelConfig myModelConfig;
private ISequenceValueMassager myIdMassager;
private boolean myConfigured;
private String myGeneratorName;
@Override
public boolean supportsBulkInsertionIdentifierGeneration() {
return myGen.supportsBulkInsertionIdentifierGeneration();
}
@Override
public String determineBulkInsertionIdentifierGenerationSelectFragment(SqlStringGenerationContext theContext) {
return myGen.determineBulkInsertionIdentifierGenerationSelectFragment(theContext);
}
@Override
public Serializable generate(SharedSessionContractImplementor theSession, Object theObject) throws HibernateException {
Long next = (Long) myGen.generate(theSession, theObject);
return myIdMassager.massage(myGeneratorName, next);
}
@Override
public void configure(Type theType, Properties theParams, ServiceRegistry theServiceRegistry) throws MappingException {
// Instantiate the ID massager
// ModelConfig should only be null when running in the DDL generation maven plugin
if (myModelConfig != null) {
myIdMassager = ReflectionUtil.newInstance(myModelConfig.getSequenceValueMassagerClass());
}
// Create a HAPI FHIR sequence style generator
myGeneratorName = theParams.getProperty(IdentifierGenerator.GENERATOR_NAME);
Validate.notBlank(myGeneratorName, "No generator name found");
Properties props = new Properties(theParams);
props.put(SequenceStyleGenerator.OPT_PARAM, StandardOptimizerDescriptor.POOLED.getExternalName());
props.put(SequenceStyleGenerator.INITIAL_PARAM, "1");
props.put(SequenceStyleGenerator.INCREMENT_PARAM, "50");
myGen.configure(theType, props, theServiceRegistry);
myConfigured = true;
}
@Override
public void registerExportables(Database database) {
myGen.registerExportables(database);
}
@Override
public void initialize(SqlStringGenerationContext context) {
myGen.initialize(context);
}
@Override
public boolean supportsJdbcBatchInserts() {
return myGen.supportsJdbcBatchInserts();
}
}

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.dao.tx;
package ca.uhn.fhir.jpa.model.dialect;
/*-
* #%L
* HAPI FHIR JPA Server
* HAPI FHIR JPA Model
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
@ -20,18 +20,25 @@ package ca.uhn.fhir.jpa.dao.tx;
* #L%
*/
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @see HapiTransactionalAspect
* @since 5.1.0
* This is an internal API and may change or disappear without notice
*
* Implementations of this interface can modify the automatically generated sequence values created by hibernate seuqnece generator
*/
@Retention(RUNTIME)
@Target({METHOD, TYPE})
public @interface HapiTransactional {
public interface ISequenceValueMassager {
Long massage(String theGeneratorName, Long theId);
final class NoopSequenceValueMassager implements ISequenceValueMassager {
@Override
public Long massage(String theGeneratorName, Long theId) {
return theId;
}
}
}

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.jpa.model.entity;
/*-
* #%L
* HAPI FHIR JPA Model
* %%
* Copyright (C) 2014 - 2022 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%
*/
import org.hl7.fhir.instance.model.api.IIdType;

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.model.entity;
import ca.uhn.fhir.context.ParserOptions;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.model.dialect.ISequenceValueMassager;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationSvc;
import com.google.common.annotations.VisibleForTesting;
@ -111,10 +112,9 @@ public class ModelConfig {
*/
private boolean myUseOrdinalDatesForDayPrecisionSearches = true;
private boolean mySuppressStringIndexingInTokens = false;
private Class<? extends ISequenceValueMassager> mySequenceValueMassagerClass;
private IPrimitiveType<Date> myPeriodIndexStartOfTime;
private IPrimitiveType<Date> myPeriodIndexEndOfTime;
private NormalizedQuantitySearchLevel myNormalizedQuantitySearchLevel;
private Set<String> myAutoVersionReferenceAtPaths = Collections.emptySet();
private Map<String, Set<String>> myTypeToAutoVersionReferenceAtPaths = Collections.emptyMap();
@ -124,16 +124,35 @@ public class ModelConfig {
private boolean myAllowMdmExpansion = false;
private boolean myAutoSupportDefaultSearchParams = true;
private boolean myIndexIdentifierOfType = false;
/**
* Constructor
*/
public ModelConfig() {
setSequenceValueMassagerClass(ISequenceValueMassager.NoopSequenceValueMassager.class);
setPeriodIndexStartOfTime(new DateTimeType(DEFAULT_PERIOD_INDEX_START_OF_TIME));
setPeriodIndexEndOfTime(new DateTimeType(DEFAULT_PERIOD_INDEX_END_OF_TIME));
setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
}
/**
* This is an internal API and may change or disappear without notice
*
* @since 6.2.0
*/
public Class<? extends ISequenceValueMassager> getSequenceValueMassagerClass() {
return mySequenceValueMassagerClass;
}
/**
* This is an internal API and may change or disappear without notice
*
* @since 6.2.0
*/
public void setSequenceValueMassagerClass(Class<? extends ISequenceValueMassager> theSequenceValueMassagerClass) {
Validate.notNull(theSequenceValueMassagerClass, "theSequenceValueMassagerClass must not be null");
mySequenceValueMassagerClass = theSequenceValueMassagerClass;
}
/**
* If set to <code>true</code> (default is <code>false</code>) the
* <code>:of-type</code> modifier on token search parameters for
@ -887,18 +906,6 @@ public class ModelConfig {
myAutoSupportDefaultSearchParams = theAutoSupportDefaultSearchParams;
}
private static void validateTreatBaseUrlsAsLocal(String theUrl) {
Validate.notBlank(theUrl, "Base URL must not be null or empty");
int starIdx = theUrl.indexOf('*');
if (starIdx != -1) {
if (starIdx != theUrl.length() - 1) {
throw new IllegalArgumentException(Msg.code(1525) + "Base URL wildcard character (*) can only appear at the end of the string: " + theUrl);
}
}
}
/**
* If enabled, the server will support cross-partition subscription.
* This subscription will be the responsible for all the requests from all the partitions on this server.
@ -929,4 +936,16 @@ public class ModelConfig {
myCrossPartitionSubscription = theAllowCrossPartitionSubscription;
}
private static void validateTreatBaseUrlsAsLocal(String theUrl) {
Validate.notBlank(theUrl, "Base URL must not be null or empty");
int starIdx = theUrl.indexOf('*');
if (starIdx != -1) {
if (starIdx != theUrl.length() - 1) {
throw new IllegalArgumentException(Msg.code(1525) + "Base URL wildcard character (*) can only appear at the end of the string: " + theUrl);
}
}
}
}

View File

@ -60,20 +60,20 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
})
/**
* Support UCUM service
* @since 5.3.0
* @since 5.3.0
*
*/
public class ResourceIndexedSearchParamQuantityNormalized extends BaseResourceIndexedSearchParamQuantity {
private static final long serialVersionUID = 1L;
@Id
@SequenceGenerator(name = "SEQ_SPIDX_QUANTITY_NRML", sequenceName = "SEQ_SPIDX_QUANTITY_NRML")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_QUANTITY_NRML")
@Column(name = "SP_ID")
private Long myId;
// Changed to double here for storing the value after converted to the CanonicalForm due to BigDecimal maps NUMBER(19,2)
// Changed to double here for storing the value after converted to the CanonicalForm due to BigDecimal maps NUMBER(19,2)
// The precision may lost even to store 1.2cm which is 0.012m in the CanonicalForm
@Column(name = "SP_VALUE", nullable = true)
@ScaledNumberField
@ -110,7 +110,7 @@ public class ResourceIndexedSearchParamQuantityNormalized extends BaseResourceIn
setHashIdentityAndUnits(source.getHashIdentityAndUnits());
setHashIdentitySystemAndUnits(source.getHashIdentitySystemAndUnits());
}
//- myValue
public Double getValue() {
return myValue;
@ -134,7 +134,7 @@ public class ResourceIndexedSearchParamQuantityNormalized extends BaseResourceIn
public void setId(Long theId) {
myId = theId;
}
@Override
public IQueryParameterType toQueryParameterType() {
return new QuantityParam(null, getValue(), getSystem(), getUnits());
@ -175,10 +175,10 @@ public class ResourceIndexedSearchParamQuantityNormalized extends BaseResourceIn
b.append(getValue(), obj.getValue());
return b.isEquals();
}
@Override
public boolean matches(IQueryParameterType theParam) {
if (!(theParam instanceof QuantityParam)) {
return false;
}
@ -191,14 +191,14 @@ public class ResourceIndexedSearchParamQuantityNormalized extends BaseResourceIn
if (quantityValue != null)
quantityDoubleValue = quantityValue.doubleValue();
String quantityUnits = defaultString(quantity.getUnits());
//-- convert the value/unit to the canonical form if any, otherwise store the original value/units pair
Pair canonicalForm = UcumServiceUtil.getCanonicalForm(quantitySystem, quantityValue, quantityUnits);
if (canonicalForm != null) {
quantityDoubleValue = Double.parseDouble(canonicalForm.getValue().asDecimal());
quantityUnits = canonicalForm.getCode();
}
}
// Only match on system if it wasn't specified
if (quantitySystem == null && isBlank(quantityUnits)) {
if (Objects.equals(getValue(), quantityDoubleValue)) {
@ -224,7 +224,7 @@ public class ResourceIndexedSearchParamQuantityNormalized extends BaseResourceIn
}
}
}
return retval;
}

View File

@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.Session;
import org.hibernate.annotations.GenerationTime;
import org.hibernate.annotations.GeneratorType;
@ -63,7 +64,7 @@ import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.SequenceGenerator;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.Version;
@ -113,7 +114,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
private boolean myHasLinks;
@Id
@SequenceGenerator(name = "SEQ_RESOURCE_ID", sequenceName = "SEQ_RESOURCE_ID")
@GenericGenerator(name = "SEQ_RESOURCE_ID", strategy = "ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESOURCE_ID")
@Column(name = "RES_ID")
@GenericField(projectable = Projectable.YES)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -27,7 +27,7 @@ import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.subscription.match.deliver.BaseSubscriptionDeliverySubscriber;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;

View File

@ -23,7 +23,7 @@ package ca.uhn.fhir.jpa.subscription.match.matcher.subscriber;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionConstants;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;

View File

@ -24,7 +24,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;

View File

@ -27,7 +27,7 @@ import ca.uhn.fhir.jpa.cache.IResourceChangeListener;
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerCache;
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.retry.Retrier;
import ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionActivatingSubscriber;

View File

@ -29,7 +29,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionMatchingStrategy;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionStrategyEvaluator;
import ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionCriteriaParser;

View File

@ -32,7 +32,7 @@ import ca.uhn.fhir.jpa.api.svc.ISearchSvc;
import ca.uhn.fhir.jpa.model.sched.HapiJob;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedConsumer;

View File

@ -22,7 +22,7 @@ package ca.uhn.fhir.jpa.subscription.util;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
import ca.uhn.fhir.rest.api.server.RequestDetails;

View File

@ -4,7 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionStrategyEvaluator;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionConstants;

View File

@ -4,7 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;

View File

@ -6,7 +6,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerCache;
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionActivatingSubscriber;
import ca.uhn.fhir.rest.api.server.IBundleProvider;

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.4-SNAPSHOT</version>
<version>6.3.5-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,12 +6,14 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IResultIterator;
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
import ca.uhn.fhir.jpa.util.BaseIterator;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.stubbing.Answer;
@ -31,9 +33,8 @@ public class BaseSearchSvc {
protected int myExpectedNumberOfSearchBuildersCreated = 2;
@Mock
protected SearchBuilderFactory<JpaPid> mySearchBuilderFactory;
@Mock
protected PlatformTransactionManager myTxManager;
@Spy
protected HapiTransactionService myTransactionService = new MockHapiTransactionService();
@Mock
protected SearchBuilder mySearchBuilder;

View File

@ -0,0 +1,22 @@
package ca.uhn.fhir.jpa.search;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.support.TransactionCallback;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.concurrent.Callable;
public class MockHapiTransactionService extends HapiTransactionService {
@Nullable
@Override
protected <T> T doExecute(ExecutionBuilder theExecutionBuilder, TransactionCallback<T> theCallback) {
return theCallback.doInTransaction(null);
}
}

Some files were not shown because too many files have changed in this diff Show More