diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java index 70f6b4a926f..112ff1a5418 100644 --- a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java @@ -58,6 +58,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 { return retVal; } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java index 14f4877c9a9..35dcc65acec 100644 --- a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java +++ b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java @@ -70,6 +70,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 { return FhirServerConfigCommon.getDataSource(env); } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { return FhirServerConfigCommon.getEntityManagerFactory(env, dataSource(), fhirContextDstu3()); diff --git a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java index 71d7388c68e..c70b393e839 100644 --- a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java +++ b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java @@ -73,6 +73,7 @@ public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 { return FhirServerConfigCommon.getDataSource(env); } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { return FhirServerConfigCommon.getEntityManagerFactory(env, dataSource(), fhirContextDstu2()); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java index 6183b9fe27a..0ab538b8225 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java @@ -188,6 +188,7 @@ public class Constants { public static final String PARAM_GRAPHQL_QUERY = "query"; public static final String HEADER_X_CACHE = "X-Cache"; public static final String HEADER_X_SECURITY_CONTEXT = "X-Security-Context"; + public static final String POWERED_BY_HEADER = "X-Powered-By"; static { CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8); diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java index d871f130efb..6001186a104 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java @@ -63,6 +63,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu2 { @Qualifier("jpaProperties") private Properties myJpaProperties; + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu3.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu3.java index 597fec99e52..6ab41b41972 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu3.java +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu3.java @@ -66,6 +66,7 @@ public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 { return retVal; } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigR4.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigR4.java index 25171dd53f1..7454ea6b677 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigR4.java +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigR4.java @@ -66,6 +66,7 @@ public class FhirServerConfigR4 extends BaseJavaConfigR4 { return retVal; } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java index 68568432944..f60839c4c90 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.config; * 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. @@ -28,6 +28,8 @@ import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl; import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor; import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor; import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketInterceptor; +import ca.uhn.fhir.jpa.util.IReindexController; +import ca.uhn.fhir.jpa.util.ReindexController; import org.hibernate.jpa.HibernatePersistenceProvider; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowired; @@ -48,6 +50,7 @@ import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler; import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean; import org.springframework.scheduling.config.ScheduledTaskRegistrar; +import javax.annotation.Nonnull; import javax.annotation.Resource; @Configuration @@ -63,14 +66,13 @@ public abstract class BaseConfig implements SchedulingConfigurer { private ApplicationContext myAppCtx; @Override - public void configureTasks(ScheduledTaskRegistrar theTaskRegistrar) { + public void configureTasks(@Nonnull ScheduledTaskRegistrar theTaskRegistrar) { theTaskRegistrar.setTaskScheduler(taskScheduler()); } @Bean(autowire = Autowire.BY_TYPE) public DatabaseBackedPagingProvider databaseBackedPagingProvider() { - DatabaseBackedPagingProvider retVal = new DatabaseBackedPagingProvider(); - return retVal; + return new DatabaseBackedPagingProvider(); } /** @@ -96,6 +98,11 @@ public abstract class BaseConfig implements SchedulingConfigurer { return new HibernateJpaDialect(); } + @Bean + private IReindexController reindexController() { + return new ReindexController(); + } + @Bean() public ScheduledExecutorFactoryBean scheduledExecutorService() { ScheduledExecutorFactoryBean b = new ScheduledExecutorFactoryBean(); @@ -167,5 +174,4 @@ public abstract class BaseConfig implements SchedulingConfigurer { return new PropertySourcesPlaceholderConfigurer(); } - } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 0d839c8dc85..054ed33d439 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao; * 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. @@ -32,6 +32,7 @@ import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider; import ca.uhn.fhir.jpa.util.DeleteConflict; import ca.uhn.fhir.jpa.util.ExpungeOptions; import ca.uhn.fhir.jpa.util.ExpungeOutcome; +import ca.uhn.fhir.jpa.util.ReindexController; import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils; import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils; import ca.uhn.fhir.model.api.*; @@ -46,7 +47,6 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor; import ca.uhn.fhir.rest.server.method.SearchMethodBinding; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.*; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.*; @@ -61,6 +61,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; +import javax.annotation.Nonnull; import javax.annotation.PostConstruct; import javax.persistence.NoResultException; import javax.persistence.TypedQuery; @@ -88,6 +89,8 @@ public abstract class BaseHapiFhirResourceDao extends B private String mySecondaryPrimaryKeyParamName; @Autowired private ISearchParamRegistry mySearchParamRegistry; + @Autowired + private ReindexController myReindexController; @Override public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) { @@ -97,7 +100,6 @@ public abstract class BaseHapiFhirResourceDao extends B throw new ResourceNotFoundException(theId); } - //@formatter:off for (BaseTag next : new ArrayList<>(entity.getTags())) { if (ObjectUtil.equals(next.getTag().getTagType(), theTagType) && ObjectUtil.equals(next.getTag().getSystem(), theScheme) && @@ -105,7 +107,6 @@ public abstract class BaseHapiFhirResourceDao extends B return; } } - //@formatter:on entity.setHasTags(true); @@ -459,7 +460,6 @@ public abstract class BaseHapiFhirResourceDao extends B return outcome; } - private void doMetaAdd(MT theMetaAdd, BaseHasResource entity) { List tags = toTagList(theMetaAdd); @@ -538,7 +538,6 @@ public abstract class BaseHapiFhirResourceDao extends B return doExpunge(getResourceName(), null, null, theExpungeOptions); } - @Override public TagList getAllResourceTags(RequestDetails theRequestDetails) { // Notify interceptors @@ -630,7 +629,7 @@ public abstract class BaseHapiFhirResourceDao extends B Integer updatedCount = txTemplate.execute(new TransactionCallback() { @Override public @NonNull - Integer doInTransaction(TransactionStatus theStatus) { + Integer doInTransaction(@Nonnull TransactionStatus theStatus) { return myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType); } }); @@ -640,6 +639,7 @@ public abstract class BaseHapiFhirResourceDao extends B } mySearchParamRegistry.requestRefresh(); + myReindexController.requestReindex(); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java index e9deeca33da..50bf987cbe1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java @@ -52,32 +52,6 @@ public class FhirResourceDaoSearchParameterDstu2 extends FhirResourceDaoDstu2 extends IFhirResourceDao { - void performReindexingPass(); - } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java index 86bd6fa3f9f..106bbd7c133 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java @@ -1,26 +1,16 @@ package ca.uhn.fhir.jpa.dao.dstu3; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.dao.BaseSearchParamExtractor; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.ISearchParamRegistry; import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4; import ca.uhn.fhir.jpa.entity.ResourceTable; -import ca.uhn.fhir.parser.DataFormatException; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.util.ElementUtil; -import org.apache.commons.lang3.time.DateUtils; import org.hl7.fhir.dstu3.model.*; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; import java.util.List; -import static org.apache.commons.lang3.StringUtils.isBlank; - /* * #%L * HAPI FHIR JPA Server @@ -30,9 +20,9 @@ import static org.apache.commons.lang3.StringUtils.isBlank; * 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. @@ -43,8 +33,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank; public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3 implements IFhirResourceDaoSearchParameter { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSearchParameterDstu3.class); - @Autowired private ISearchParamRegistry mySearchParamRegistry; @@ -57,31 +45,6 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3 type = theResource.getType(); - + FhirResourceDaoSearchParameterR4.validateSearchParam(type, status, base, expression, context); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java index 5133859d649..56ed7fff194 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java @@ -21,6 +21,12 @@ package ca.uhn.fhir.jpa.entity; */ import ca.uhn.fhir.model.api.IQueryParameterType; +import ca.uhn.fhir.util.UrlUtil; +import com.google.common.base.Charsets; +import com.google.common.hash.HashCode; +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; import org.hibernate.search.annotations.ContainedIn; import org.hibernate.search.annotations.Field; @@ -30,6 +36,10 @@ import java.util.Date; @MappedSuperclass public abstract class BaseResourceIndexedSearchParam implements Serializable { + /** Don't change this without careful consideration. You will break existing hashes! */ + private static final HashFunction HASH_FUNCTION = Hashing.murmur3_128(0); + /** Don't make this public 'cause nobody better touch it! */ + private static final byte[] DELIMITER_BYTES = "|".getBytes(Charsets.UTF_8); static final int MAX_SP_NAME = 100; @@ -68,14 +78,23 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable { } public void setParamName(String theName) { + clearHashes(); myParamName = theName; } + /** + * Subclasses may override + */ + protected void clearHashes() { + // nothing + } + public ResourceTable getResource() { return myResource; } public BaseResourceIndexedSearchParam setResource(ResourceTable theResource) { + clearHashes(); myResource = theResource; myResourceType = theResource.getResourceType(); return this; @@ -107,4 +126,23 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable { } public abstract IQueryParameterType toQueryParameterType(); + + /** + * Applies a fast and consistent hashing algorithm to a set of strings + */ + static long hash(String... theValues) { + Hasher hasher = HASH_FUNCTION.newHasher(); + + for (String next : theValues) { + next = UrlUtil.escapeUrlParam(next); + byte[] bytes = next.getBytes(Charsets.UTF_8); + hasher.putBytes(bytes); + hasher.putBytes(DELIMITER_BYTES); + } + + HashCode hashCode = hasher.hash(); + return hashCode.asLong(); + } + + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedCompositeStringUnique.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedCompositeStringUnique.java index 2a185ea0435..ff8c6eae3b7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedCompositeStringUnique.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedCompositeStringUnique.java @@ -41,7 +41,6 @@ public class ResourceIndexedCompositeStringUnique implements Comparable 255 so be careful here + * Note that MYSQL chokes on unique indexes for lengths > 255 so be careful here */ public static final int MAX_LENGTH = 200; - + public static final int HASH_PREFIX_LENGTH = 1; private static final long serialVersionUID = 1L; - @Id @SequenceGenerator(name = "SEQ_SPIDX_STRING", sequenceName = "SEQ_SPIDX_STRING") @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_STRING") @@ -116,6 +117,16 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP @Column(name = "SP_VALUE_NORMALIZED", length = MAX_LENGTH, nullable = true) private String myValueNormalized; + /** + * @since 3.4.0 - At some point this should be made not-null + */ + @Column(name = "HASH_NORM_PREFIX", nullable = true) + private Long myHashNormalizedPrefix; + /** + * @since 3.4.0 - At some point this should be made not-null + */ + @Column(name = "HASH_EXACT", nullable = true) + private Long myHashExact; public ResourceIndexedSearchParamString() { super(); @@ -128,6 +139,20 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP setValueExact(theValueExact); } + @PrePersist + public void calculateHashes() { + if (myHashNormalizedPrefix == null) { + setHashNormalizedPrefix(hash(getResourceType(), getParamName(), left(getValueNormalized(), HASH_PREFIX_LENGTH))); + setHashExact(hash(getResourceType(), getParamName(), getValueExact())); + } + } + + @Override + protected void clearHashes() { + myHashNormalizedPrefix = null; + myHashExact = null; + } + @Override public boolean equals(Object theObj) { if (this == theObj) { @@ -144,9 +169,29 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP b.append(getParamName(), obj.getParamName()); b.append(getResource(), obj.getResource()); b.append(getValueExact(), obj.getValueExact()); + b.append(getHashNormalizedPrefix(), obj.getHashNormalizedPrefix()); + b.append(getHashExact(), obj.getHashExact()); return b.isEquals(); } + public Long getHashExact() { + calculateHashes(); + return myHashExact; + } + + public void setHashExact(Long theHashExact) { + myHashExact = theHashExact; + } + + public Long getHashNormalizedPrefix() { + calculateHashes(); + return myHashNormalizedPrefix; + } + + public void setHashNormalizedPrefix(Long theHashNormalizedPrefix) { + myHashNormalizedPrefix = theHashNormalizedPrefix; + } + @Override protected Long getId() { return myId; @@ -180,6 +225,8 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP b.append(getParamName()); b.append(getResource()); b.append(getValueExact()); + b.append(getHashNormalizedPrefix()); + b.append(getHashExact()); return b.toHashCode(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamToken.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamToken.java index 7b6dcc8534a..27b6a17f390 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamToken.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamToken.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.entity; * 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. @@ -31,7 +31,6 @@ import org.hibernate.search.annotations.Field; import javax.persistence.*; -//@formatter:off @Embeddable @Entity @Table(name = "HFJ_SPIDX_TOKEN", indexes = { @@ -40,12 +39,12 @@ import javax.persistence.*; @Index(name = "IDX_SP_TOKEN_UPDATED", columnList = "SP_UPDATED"), @Index(name = "IDX_SP_TOKEN_RESID", columnList = "RES_ID") }) -//@formatter:on public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam { public static final int MAX_LENGTH = 200; private static final long serialVersionUID = 1L; + @Field() @Column(name = "SP_SYSTEM", nullable = true, length = MAX_LENGTH) public String mySystem; @@ -57,16 +56,58 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_TOKEN") @Column(name = "SP_ID") private Long myId; + /** + * @since 3.4.0 - At some point this should be made not-null + */ + @Column(name = "HASH_SYS", nullable = true) + private Long myHashSystem; + /** + * @since 3.4.0 - At some point this should be made not-null + */ + @Column(name = "HASH_SYS_AND_VALUE", nullable = true) + private Long myHashSystemAndValue; + /** + * @since 3.4.0 - At some point this should be made not-null + */ + @Column(name = "HASH_VALUE", nullable = true) + private Long myHashValue; + + /** + * Constructor + */ public ResourceIndexedSearchParamToken() { + super(); } + /** + * Constructor + */ public ResourceIndexedSearchParamToken(String theName, String theSystem, String theValue) { + super(); setParamName(theName); setSystem(theSystem); setValue(theValue); } + + @PrePersist + public void calculateHashes() { + if (myHashSystem == null) { + setHashSystem(hash(getResourceType(), getParamName(), getSystem())); + setHashSystemAndValue(hash(getResourceType(), getParamName(), getSystem(), getValue())); + setHashValue(hash(getResourceType(), getParamName(), getValue())); + } + } + + + @Override + protected void clearHashes() { + myHashSystem = null; + myHashSystemAndValue = null; + myHashValue = null; + } + @Override public boolean equals(Object theObj) { if (this == theObj) { @@ -84,9 +125,40 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa b.append(getResource(), obj.getResource()); b.append(getSystem(), obj.getSystem()); b.append(getValue(), obj.getValue()); + b.append(getHashSystem(), obj.getHashSystem()); + b.append(getHashSystemAndValue(), obj.getHashSystemAndValue()); + b.append(getHashValue(), obj.getHashValue()); return b.isEquals(); } + public Long getHashSystem() { + calculateHashes(); + return myHashSystem; + } + + public void setHashSystem(Long theHashSystem) { + myHashSystem = theHashSystem; + } + + public Long getHashSystemAndValue() { + calculateHashes(); + return myHashSystemAndValue; + } + + public void setHashSystemAndValue(Long theHashSystemAndValue) { + calculateHashes(); + myHashSystemAndValue = theHashSystemAndValue; + } + + public Long getHashValue() { + calculateHashes(); + return myHashValue; + } + + public void setHashValue(Long theHashValue) { + myHashValue = theHashValue; + } + @Override protected Long getId() { return myId; @@ -97,6 +169,7 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa } public void setSystem(String theSystem) { + clearHashes(); mySystem = StringUtils.defaultIfBlank(theSystem, null); } @@ -105,6 +178,7 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa } public void setValue(String theValue) { + clearHashes(); myValue = StringUtils.defaultIfBlank(theValue, null); } @@ -115,9 +189,13 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa b.append(getResource()); b.append(getSystem()); b.append(getValue()); + b.append(getHashSystem()); + b.append(getHashSystemAndValue()); + b.append(getHashValue()); return b.toHashCode(); } + @Override public IQueryParameterType toQueryParameterType() { return new TokenParam(getSystem(), getValue()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java index c1f915ea043..72fd8bcbf6d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.entity; * 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. @@ -30,7 +30,6 @@ import org.hibernate.search.annotations.Field; import javax.persistence.*; -//@formatter:off @Embeddable @Entity @Table(name = "HFJ_SPIDX_URI", indexes = { @@ -39,11 +38,10 @@ import javax.persistence.*; @Index(name = "IDX_SP_URI_UPDATED", columnList = "SP_UPDATED"), @Index(name = "IDX_SP_URI_COORDS", columnList = "RES_ID") }) -//@formatter:on public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchParam { /* - * Note that MYSQL chokes on unique indexes for lengths > 255 so be careful here + * Note that MYSQL chokes on unique indexes for lengths > 255 so be careful here */ public static final int MAX_LENGTH = 255; @@ -56,15 +54,38 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_URI") @Column(name = "SP_ID") private Long myId; + /** + * @since 3.4.0 - At some point this should be made not-null + */ + @Column(name = "HASH_URI", nullable = true) + private Long myHashUri; + /** + * Constructor + */ public ResourceIndexedSearchParamUri() { } + /** + * Constructor + */ public ResourceIndexedSearchParamUri(String theName, String theUri) { setParamName(theName); setUri(theUri); } + @PrePersist + public void calculateHashes() { + if (myHashUri == null) { + setHashUri(hash(getResourceType(), getParamName(), getUri())); + } + } + + @Override + protected void clearHashes() { + myHashUri = null; + } + @Override public boolean equals(Object theObj) { if (this == theObj) { @@ -81,9 +102,19 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara b.append(getParamName(), obj.getParamName()); b.append(getResource(), obj.getResource()); b.append(getUri(), obj.getUri()); + b.append(getHashUri(), obj.getHashUri()); return b.isEquals(); } + public Long getHashUri() { + calculateHashes(); + return myHashUri; + } + + public void setHashUri(Long theHashUri) { + myHashUri = theHashUri; + } + @Override protected Long getId() { return myId; @@ -103,6 +134,7 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara b.append(getParamName()); b.append(getResource()); b.append(getUri()); + b.append(getHashUri()); return b.toHashCode(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java index c5afc603eb8..d8a7645459b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java @@ -434,8 +434,9 @@ public class ResourceTable extends BaseHasResource implements Serializable { return myResourceType; } - public void setResourceType(String theResourceType) { + public ResourceTable setResourceType(String theResourceType) { myResourceType = theResourceType; + return this; } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/IReindexController.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/IReindexController.java new file mode 100644 index 00000000000..76376e24bff --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/IReindexController.java @@ -0,0 +1,5 @@ +package ca.uhn.fhir.jpa.util; + +public interface IReindexController { + void requestReindex(); +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ReindexController.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ReindexController.java new file mode 100644 index 00000000000..eca43b23730 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ReindexController.java @@ -0,0 +1,90 @@ +package ca.uhn.fhir.jpa.util; + +import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.jpa.dao.IFhirSystemDao; +import org.apache.commons.lang3.time.DateUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.concurrent.Semaphore; + +public class ReindexController implements IReindexController { + + private static final Logger ourLog = LoggerFactory.getLogger(ReindexController.class); + private final Semaphore myReindexingLock = new Semaphore(1); + @Autowired + private DaoConfig myDaoConfig; + @Autowired + private IFhirSystemDao mySystemDao; + private Long myDontReindexUntil; + + /** + * This method is called once per minute to perform any required re-indexing. + *

+ * If nothing if found that requires reindexing, the query will not fire again for + * a longer amount of time. + *

+ * During most passes this will just check and find that there are no resources + * requiring re-indexing. In that case the method just returns immediately. + * If the search finds that some resources require reindexing, the system will + * do a bunch of reindexing and then return. + */ + @Scheduled(fixedDelay = DateUtils.MILLIS_PER_MINUTE) + @Transactional(propagation = Propagation.NEVER) + public void performReindexingPass() { + if (myDaoConfig.isSchedulingDisabled()) { + return; + } + + synchronized (this) { + if (myDontReindexUntil == null && myDontReindexUntil > System.currentTimeMillis()) { + return; + } + } + + if (!myReindexingLock.tryAcquire()) { + ourLog.trace("Not going to reindex in parallel threads"); + return; + } + Integer count; + try { + count = mySystemDao.performReindexingPass(100); + + for (int i = 0; i < 50 && count != null && count != 0; i++) { + count = mySystemDao.performReindexingPass(100); + try { + Thread.sleep(DateUtils.MILLIS_PER_SECOND); + } catch (InterruptedException e) { + break; + } + } + } finally { + myReindexingLock.release(); + } + + synchronized (this) { + if (count == null) { + myDontReindexUntil = System.currentTimeMillis() + DateUtils.MILLIS_PER_HOUR; + } else { + myDontReindexUntil = null; + } + } + + } + + /** + * Calling this will cause a reindex loop to be triggered sooner that it would otherwise + */ + @Override + public void requestReindex() { + synchronized (this) { + myDontReindexUntil = null; + } + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java index b215201ce9a..a7d9cccfe12 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java @@ -133,6 +133,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 { return retVal; } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java index e1f73dcdca3..0a8df9f8bc5 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java @@ -115,6 +115,7 @@ public class TestR4Config extends BaseJavaConfigR4 { return dataSource; } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantityTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantityTest.java new file mode 100644 index 00000000000..bb6a3cb8383 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantityTest.java @@ -0,0 +1,38 @@ +package ca.uhn.fhir.jpa.entity; + +import org.junit.Test; + +import java.math.BigDecimal; + +import static org.junit.Assert.*; + +public class ResourceIndexedSearchParamQuantityTest { + + private ResourceIndexedSearchParamQuantity createParam(String theParamName, String theValue, String theSystem, String theUnits) { + ResourceIndexedSearchParamQuantity token = new ResourceIndexedSearchParamQuantity(theParamName, new BigDecimal(theValue), theSystem, theUnits); + token.setResource(new ResourceTable().setResourceType("Patient")); + return token; + } + + @Test + public void testHashFunctions() { + ResourceIndexedSearchParamQuantity token = createParam("NAME", "123.001", "value", "VALUE"); + + // Make sure our hashing function gives consistent results + assertEquals(945335027461836896L, token.getHashUnitsAndValPrefix().longValue()); + assertEquals(5549105497508660145L, token.getHashValPrefix().longValue()); + } + + @Test + public void testValueTrimming() { + assertEquals(7265149425397186226L, createParam("NAME", "401.001", "value", "VALUE").getHashUnitsAndValPrefix().longValue()); + assertEquals(7265149425397186226L, createParam("NAME", "401.99999", "value", "VALUE").getHashUnitsAndValPrefix().longValue()); + assertEquals(7265149425397186226L, createParam("NAME", "401", "value", "VALUE").getHashUnitsAndValPrefix().longValue()); + // Should be different + assertEquals(-8387917096585386046L, createParam("NAME", "400.9999999", "value", "VALUE").getHashUnitsAndValPrefix().longValue()); + // Should be different + assertEquals(8819656626732693650L, createParam("NAME", "402.000000", "value", "VALUE").getHashUnitsAndValPrefix().longValue()); + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamStringTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamStringTest.java new file mode 100644 index 00000000000..78722df4fda --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamStringTest.java @@ -0,0 +1,31 @@ +package ca.uhn.fhir.jpa.entity; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class ResourceIndexedSearchParamStringTest { + + @Test + public void testHashFunctions() { + ResourceIndexedSearchParamString token = new ResourceIndexedSearchParamString("NAME", "value", "VALUE"); + token.setResource(new ResourceTable().setResourceType("Patient")); + + // Make sure our hashing function gives consistent results + assertEquals(6598082761639188617L, token.getHashNormalizedPrefix().longValue()); + assertEquals(-1970227166134682431L, token.getHashExact().longValue()); + } + + @Test + public void testHashFunctionsPrefixOnly() { + ResourceIndexedSearchParamString token = new ResourceIndexedSearchParamString("NAME", "vZZZZZZZZZZZZZZZZ", "VZZZZZZzzzZzzzZ"); + token.setResource(new ResourceTable().setResourceType("Patient")); + + // Should be the same as in testHashFunctions() + assertEquals(6598082761639188617L, token.getHashNormalizedPrefix().longValue()); + + // Should be different from testHashFunctions() + assertEquals(-1970227166134682431L, token.getHashExact().longValue()); + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamTokenTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamTokenTest.java new file mode 100644 index 00000000000..50f93a8617c --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamTokenTest.java @@ -0,0 +1,31 @@ +package ca.uhn.fhir.jpa.entity; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ResourceIndexedSearchParamTokenTest { + + @Test + public void testHashFunctions() { + ResourceIndexedSearchParamToken token = new ResourceIndexedSearchParamToken("NAME", "SYSTEM", "VALUE"); + token.setResource(new ResourceTable().setResourceType("Patient")); + + // Make sure our hashing function gives consistent results + assertEquals(-8558989679010582575L, token.getHashSystem().longValue()); + assertEquals(-8644532105141886455L, token.getHashSystemAndValue().longValue()); + assertEquals(-1970227166134682431L, token.getHashValue().longValue()); + } + + @Test + public void testHashFunctionsWithOverlapNames() { + ResourceIndexedSearchParamToken token = new ResourceIndexedSearchParamToken("NAME", "SYSTEM", "VALUE"); + token.setResource(new ResourceTable().setResourceType("Patient")); + + // Make sure our hashing function gives consistent results + assertEquals(-8558989679010582575L, token.getHashSystem().longValue()); + assertEquals(-8644532105141886455L, token.getHashSystemAndValue().longValue()); + assertEquals(-1970227166134682431L, token.getHashValue().longValue()); + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUriTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUriTest.java new file mode 100644 index 00000000000..6b56a2287c9 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUriTest.java @@ -0,0 +1,19 @@ +package ca.uhn.fhir.jpa.entity; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class ResourceIndexedSearchParamUriTest { + + @Test + public void testHashFunctions() { + ResourceIndexedSearchParamUri token = new ResourceIndexedSearchParamUri("NAME", "http://example.com"); + token.setResource(new ResourceTable().setResourceType("Patient")); + + // Make sure our hashing function gives consistent results + assertEquals(-6132951326739875838L, token.getHashUri().longValue()); + } + + +} diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java index f16348d4401..8a2b429b2f7 100644 --- a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java +++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java @@ -57,6 +57,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 { return retVal; } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java index 784ddc6b485..6d71dedc40e 100644 --- a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java +++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java @@ -58,6 +58,7 @@ public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 { return retVal; } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/elasticsearch/FhirServerConfig.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/elasticsearch/FhirServerConfig.java index 1105d42f7c6..5bc09d6419c 100644 --- a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/elasticsearch/FhirServerConfig.java +++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/elasticsearch/FhirServerConfig.java @@ -53,6 +53,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 { return retVal; } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java index 27727d4e2ac..b71ef68744c 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java @@ -90,6 +90,7 @@ public class TdlDstu2Config extends BaseJavaConfigDstu2 { return retVal; } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu3Config.java index 760e267a050..8e53ff6a7df 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu3Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu3Config.java @@ -70,6 +70,7 @@ public class TdlDstu3Config extends BaseJavaConfigDstu3 { return retVal; } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java index 2e8fa93af4e..a1f9138e93f 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java @@ -92,6 +92,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 { return retVal; } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java index 84f32331a81..9c90da410f6 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java @@ -91,6 +91,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 { return retVal; } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java index 13ff353a8e5..89256cc3195 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java @@ -86,6 +86,7 @@ public class TestR4Config extends BaseJavaConfigR4 { return retVal; } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index b9e398b4a56..52a93f98617 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -119,7 +119,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer myTypeToProvider = new HashMap<>(); private boolean myUncompressIncomingContents = true; @@ -159,7 +159,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer poweredByAttributes = createPoweredByAttributes(); for (ListIterator iter = poweredByAttributes.listIterator(); iter.hasNext(); ) { @@ -232,10 +241,33 @@ public class RestfulServer extends HttpServlet implements IRestfulServer theList) { myInterceptors.clear(); if (theList != null) { - myInterceptors.addAll(Arrays.asList(theList)); + myInterceptors.addAll(theList); } } @@ -572,11 +604,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer theProviders) { - myPlainProviders.clear(); - if (theProviders != null) { - myPlainProviders.addAll(theProviders); - } + public void setPlainProviders(Object... theProv) { + setPlainProviders(Arrays.asList(theProv)); } /** @@ -606,10 +635,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer theResourceProviders) { + public void setResourceProviders(IResourceProvider... theResourceProviders) { myResourceProviders.clear(); if (theResourceProviders != null) { - myResourceProviders.addAll(theResourceProviders); + myResourceProviders.addAll(Arrays.asList(theResourceProviders)); } } @@ -632,8 +661,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer providers) { + public void registerProviders(Collection providers) { myProviderRegistrationMutex.lock(); try { if (!myStarted) { @@ -1323,9 +1349,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer providers, boolean inInit) { - List newResourceProviders = new ArrayList(); - List newPlainProviders = new ArrayList(); + protected void registerProviders(Collection providers, boolean inInit) { + List newResourceProviders = new ArrayList<>(); + List newPlainProviders = new ArrayList<>(); ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext()); if (providers != null) { @@ -1392,7 +1418,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer clazz = theProvider.getClass(); Class supertype = clazz.getSuperclass(); - Collection resourceNames = new ArrayList(); + Collection resourceNames = new ArrayList<>(); while (!Object.class.equals(supertype)) { removeResourceMethods(theProvider, supertype, resourceNames); supertype = supertype.getSuperclass(); @@ -1468,12 +1494,18 @@ public class RestfulServer extends HttpServlet implements IRestfulServer theList) { + public void setInterceptors(IServerInterceptor... theList) { myInterceptors.clear(); if (theList != null) { - myInterceptors.addAll(theList); + myInterceptors.addAll(Arrays.asList(theList)); } } @@ -1497,8 +1529,11 @@ public class RestfulServer extends HttpServlet implements IRestfulServer theProviders) { + myPlainProviders.clear(); + if (theProviders != null) { + myPlainProviders.addAll(theProviders); + } } /** @@ -1516,10 +1551,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer theResourceProviders) { myResourceProviders.clear(); if (theResourceProviders != null) { - myResourceProviders.addAll(Arrays.asList(theResourceProviders)); + myResourceProviders.addAll(theResourceProviders); } } @@ -1539,13 +1574,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer providerList = new ArrayList(1); + Collection providerList = new ArrayList<>(1); providerList.add(provider); unregisterProviders(providerList); } @@ -1553,11 +1585,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer providers) throws Exception { + public void unregisterProviders(Collection providers) { ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext()); if (providers != null) { for (Object provider : providers) { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java index 2bea53030a5..1cb55f38a44 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java @@ -128,9 +128,7 @@ public class SearchR4Test { @Test public void testIncludeSingleParameter() throws Exception { HttpGet httpGet; - String linkNext; Bundle bundle; - String linkSelf; // No include specified httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationRequest"); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 5091c0a47a7..31b3eda3346 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -17,6 +17,20 @@ ]]> + + Several enhancements have been made to the JPA server index + tables. These enhancements consist of new colums that will be + used in a future version of HAPI FHIR to significantly decrease + the amount of space required for indexes on token and string index + types. +
]]> + These new columns are not yet used in HAPI FHIR 3.4.0 but will be + enabled in HAPI FHIR 3.5.0. Anyone upgrading to HAPI FHIR 3.4.0 (or above) + is recommended to invoke the following SQL statement on their + database in order to reindex all data in a background job: + ]]> + update HFJ_RESOURCE set SP_INDEX_STATUS = null;]]> +
R4 structures have been updated to the latest definitions (SVN 13732) @@ -226,6 +240,11 @@ An issue in the narrative generator template for the CodeableConcept datatype was corrected. Thanks to @RuthAlk for the pull request! + + The JPA server automatic reindexing process has been tweaked so that it no + longer runs once per minute (this was a heavy strain on large databases) + but will instead run once an hour unless triggered for some reason. +