001/*- 002 * #%L 003 * HAPI FHIR JPA Model 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.model.entity; 021 022import ca.uhn.fhir.context.ParserOptions; 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.jpa.util.ISequenceValueMassager; 025import ca.uhn.fhir.model.api.TemporalPrecisionEnum; 026import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationSvc; 027import ca.uhn.fhir.util.HapiExtensions; 028import com.google.common.annotations.VisibleForTesting; 029import org.apache.commons.collections4.CollectionUtils; 030import org.apache.commons.lang3.Validate; 031import org.hl7.fhir.dstu2.model.Subscription; 032import org.hl7.fhir.instance.model.api.IPrimitiveType; 033import org.hl7.fhir.r4.model.DateTimeType; 034 035import java.util.Arrays; 036import java.util.Collections; 037import java.util.Date; 038import java.util.HashMap; 039import java.util.HashSet; 040import java.util.Map; 041import java.util.Set; 042 043import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; 044 045/** 046 * This class contains configuration options common to all hapi-fhir-storage implementations. 047 * Ultimately it should live in that project 048 */ 049public class StorageSettings { 050 /** 051 * @since 5.6.0 052 */ 053 // Thread Pool size used by batch in bundle 054 public static final int DEFAULT_BUNDLE_BATCH_POOL_SIZE = 20; // 1 for single thread 055 056 public static final int DEFAULT_BUNDLE_BATCH_MAX_POOL_SIZE = 100; // 1 for single thread 057 /** 058 * Default {@link #getTreatReferencesAsLogical() logical URL bases}. Includes the following 059 * values: 060 * <ul> 061 * <li><code>"http://hl7.org/fhir/valueset-*"</code></li> 062 * <li><code>"http://hl7.org/fhir/codesystem-*"</code></li> 063 * <li><code>"http://hl7.org/fhir/StructureDefinition/*"</code></li> 064 * </ul> 065 */ 066 public static final Set<String> DEFAULT_LOGICAL_BASE_URLS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( 067 "http://hl7.org/fhir/ValueSet/*", 068 "http://hl7.org/fhir/CodeSystem/*", 069 "http://hl7.org/fhir/valueset-*", 070 "http://hl7.org/fhir/codesystem-*", 071 "http://hl7.org/fhir/StructureDefinition/*"))); 072 073 public static final String DEFAULT_WEBSOCKET_CONTEXT_PATH = "/websocket"; 074 /* 075 * <p> 076 * Note the following database documented limitations: 077 * <ul> 078 * <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li> 079 * <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li> 080 * <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li> 081 * <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li> 082 * <li>H2: datetime2 Low Value -4713 and High Value 9999</li> 083 * </ul> 084 * </p> 085 */ 086 protected static final String DEFAULT_PERIOD_INDEX_START_OF_TIME = "1001-01-01"; 087 protected static final String DEFAULT_PERIOD_INDEX_END_OF_TIME = "9000-01-01"; 088 private static final Integer DEFAULT_MAXIMUM_TRANSACTION_BUNDLE_SIZE = null; 089 /** 090 * update setter javadoc if default changes 091 */ 092 private boolean myAllowContainsSearches = false; 093 094 private boolean myAllowExternalReferences = false; 095 private Set<String> myTreatBaseUrlsAsLocal = new HashSet<>(); 096 private Set<String> myTreatReferencesAsLogical = new HashSet<>(DEFAULT_LOGICAL_BASE_URLS); 097 private boolean myDefaultSearchParamsCanBeOverridden = true; 098 private Set<Subscription.SubscriptionChannelType> mySupportedSubscriptionTypes = new HashSet<>(); 099 private boolean myAutoCreatePlaceholderReferenceTargets; 100 private boolean myCrossPartitionSubscriptionEnabled = true; 101 private Integer myBundleBatchPoolSize = DEFAULT_BUNDLE_BATCH_POOL_SIZE; 102 private Integer myBundleBatchMaxPoolSize = DEFAULT_BUNDLE_BATCH_MAX_POOL_SIZE; 103 private boolean myEnableInMemorySubscriptionMatching = true; 104 private boolean myTriggerSubscriptionsForNonVersioningChanges; 105 private boolean myMassIngestionMode; 106 private Integer myMaximumTransactionBundleSize = DEFAULT_MAXIMUM_TRANSACTION_BUNDLE_SIZE; 107 private boolean myNormalizeTerminologyForBulkExportJobs = false; 108 private String myEmailFromAddress = "noreply@unknown.com"; 109 private String myWebsocketContextPath = DEFAULT_WEBSOCKET_CONTEXT_PATH; 110 /** 111 * Update setter javadoc if default changes. 112 */ 113 private boolean myUseOrdinalDatesForDayPrecisionSearches = true; 114 115 private boolean mySuppressStringIndexingInTokens = false; 116 private Class<? extends ISequenceValueMassager> mySequenceValueMassagerClass; 117 private IPrimitiveType<Date> myPeriodIndexStartOfTime; 118 private IPrimitiveType<Date> myPeriodIndexEndOfTime; 119 private NormalizedQuantitySearchLevel myNormalizedQuantitySearchLevel; 120 private Set<String> myAutoVersionReferenceAtPaths = Collections.emptySet(); 121 private Map<String, Set<String>> myTypeToAutoVersionReferenceAtPaths = Collections.emptyMap(); 122 private boolean myRespectVersionsForSearchIncludes; 123 private boolean myIndexOnUpliftedRefchains = false; 124 private boolean myIndexOnContainedResources = false; 125 private boolean myIndexOnContainedResourcesRecursively = false; 126 private boolean myAllowMdmExpansion = false; 127 private boolean myAutoSupportDefaultSearchParams = true; 128 private boolean myIndexIdentifierOfType = false; 129 private IndexEnabledEnum myIndexMissingFieldsEnabled = IndexEnabledEnum.DISABLED; 130 131 /** 132 * @since 6.8.0 133 * Prevents any non IN-MEMORY Search params from being created by users. 134 */ 135 private boolean myAllowOnlyInMemorySubscriptions = false; 136 137 /** 138 * Since 6.4.0 139 */ 140 private boolean myQualifySubscriptionMatchingChannelName = true; 141 /** 142 * Should the {@literal _lamguage} SearchParameter be supported 143 * on this server? 144 * 145 * @since 7.0.0 146 */ 147 private boolean myLanguageSearchParameterEnabled = false; 148 149 /** 150 * If set to false, all resource types will be installed via package installer, regardless of their status. 151 * Otherwise, resources will be filtered based on status according to some criteria which can be found in 152 * <code>PackageInstallerSvcImpl#isValidResourceStatusForPackageUpload<code> 153 * @since 7.0.0 154 */ 155 private boolean myValidateResourceStatusForPackageUpload = true; 156 157 /** 158 * If set to true, the server will prevent the creation of Subscriptions which cannot be evaluated IN-MEMORY. This can improve 159 * overall server performance. 160 * 161 * @since 6.8.0 162 */ 163 public void setOnlyAllowInMemorySubscriptions(boolean theAllowOnlyInMemorySearchParams) { 164 myAllowOnlyInMemorySubscriptions = theAllowOnlyInMemorySearchParams; 165 } 166 167 /** 168 * If set to true, the server will prevent the creation of Subscriptions which cannot be evaluated IN-MEMORY. This can improve 169 * overall server performance. 170 * 171 * @since 6.8.0 172 * @return Returns the value of {@link #setOnlyAllowInMemorySubscriptions(boolean)} 173 */ 174 public boolean isOnlyAllowInMemorySubscriptions() { 175 return myAllowOnlyInMemorySubscriptions; 176 } 177 /** 178 * Constructor 179 */ 180 public StorageSettings() { 181 setSequenceValueMassagerClass(ISequenceValueMassager.NoopSequenceValueMassager.class); 182 setPeriodIndexStartOfTime(new DateTimeType(DEFAULT_PERIOD_INDEX_START_OF_TIME)); 183 setPeriodIndexEndOfTime(new DateTimeType(DEFAULT_PERIOD_INDEX_END_OF_TIME)); 184 setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED); 185 } 186 187 /** 188 * When creating or updating a resource: If this property is set to <code>true</code> 189 * (default is <code>false</code>), if the resource has a reference to another resource 190 * on the local server but that reference does not exist, a placeholder resource will be 191 * created. 192 * <p> 193 * In other words, if an observation with subject <code>Patient/FOO</code> is created, but 194 * there is no resource called <code>Patient/FOO</code> on the server, this property causes 195 * an empty patient with ID "FOO" to be created in order to prevent this operation 196 * from failing. 197 * </p> 198 * <p> 199 * This property can be useful in cases where replication between two servers is wanted. 200 * Note however that references containing purely numeric IDs will not be auto-created 201 * as they are never allowed to be client supplied in HAPI FHIR JPA. 202 * <p> 203 * All placeholder resources created in this way have an extension 204 * with the URL {@link HapiExtensions#EXT_RESOURCE_PLACEHOLDER} and the value "true". 205 * </p> 206 */ 207 public boolean isAutoCreatePlaceholderReferenceTargets() { 208 return myAutoCreatePlaceholderReferenceTargets; 209 } 210 211 /** 212 * When creating or updating a resource: If this property is set to <code>true</code> 213 * (default is <code>false</code>), if the resource has a reference to another resource 214 * on the local server but that reference does not exist, a placeholder resource will be 215 * created. 216 * <p> 217 * In other words, if an observation with subject <code>Patient/FOO</code> is created, but 218 * there is no resource called <code>Patient/FOO</code> on the server, this property causes 219 * an empty patient with ID "FOO" to be created in order to prevent this operation 220 * from failing. 221 * </p> 222 * <p> 223 * This property can be useful in cases where replication between two servers is wanted. 224 * Note however that references containing purely numeric IDs will not be auto-created 225 * as they are never allowed to be client supplied in HAPI FHIR JPA. 226 * <p> 227 * All placeholder resources created in this way have an extension 228 * with the URL {@link HapiExtensions#EXT_RESOURCE_PLACEHOLDER} and the value "true". 229 * </p> 230 */ 231 public void setAutoCreatePlaceholderReferenceTargets(boolean theAutoCreatePlaceholderReferenceTargets) { 232 myAutoCreatePlaceholderReferenceTargets = theAutoCreatePlaceholderReferenceTargets; 233 } 234 235 /** 236 * Get the batch transaction thread pool size. 237 * 238 * @since 5.6.0 239 */ 240 public Integer getBundleBatchPoolSize() { 241 return myBundleBatchPoolSize; 242 } 243 244 /** 245 * Set the batch transaction thread pool size. The default is @see {@link #DEFAULT_BUNDLE_BATCH_POOL_SIZE} 246 * set pool size to 1 for single thread 247 * 248 * @since 5.6.0 249 */ 250 public void setBundleBatchPoolSize(Integer theBundleBatchPoolSize) { 251 this.myBundleBatchPoolSize = theBundleBatchPoolSize; 252 } 253 254 /** 255 * Get the batch transaction thread max pool size. 256 * set max pool size to 1 for single thread 257 * 258 * @since 5.6.0 259 */ 260 public Integer getBundleBatchMaxPoolSize() { 261 return myBundleBatchMaxPoolSize; 262 } 263 264 /** 265 * Set the batch transaction thread pool size. The default is @see {@link #DEFAULT_BUNDLE_BATCH_MAX_POOL_SIZE} 266 * 267 * @since 5.6.0 268 */ 269 public void setBundleBatchMaxPoolSize(Integer theBundleBatchMaxPoolSize) { 270 this.myBundleBatchMaxPoolSize = theBundleBatchMaxPoolSize; 271 } 272 273 /** 274 * If set to <code>false</code> (default is true) the server will not use 275 * in-memory subscription searching and instead use the database matcher for all subscription 276 * criteria matching. 277 * <p> 278 * When there are subscriptions registered 279 * on the server, the default behaviour is to compare the changed resource to the 280 * subscription criteria directly in-memory without going out to the database. 281 * Certain types of subscription criteria, e.g. chained references of queries with 282 * qualifiers or prefixes, are not supported by the in-memory matcher and will fall back 283 * to a database matcher. 284 * <p> 285 * The database matcher performs a query against the 286 * database by prepending ?id=XYZ to the subscription criteria where XYZ is the id of the changed entity 287 * 288 * @since 3.6.1 289 */ 290 public boolean isEnableInMemorySubscriptionMatching() { 291 return myEnableInMemorySubscriptionMatching; 292 } 293 294 /** 295 * If set to <code>false</code> (default is true) the server will not use 296 * in-memory subscription searching and instead use the database matcher for all subscription 297 * criteria matching. 298 * <p> 299 * When there are subscriptions registered 300 * on the server, the default behaviour is to compare the changed resource to the 301 * subscription criteria directly in-memory without going out to the database. 302 * Certain types of subscription criteria, e.g. chained references of queries with 303 * qualifiers or prefixes, are not supported by the in-memory matcher and will fall back 304 * to a database matcher. 305 * <p> 306 * The database matcher performs a query against the 307 * database by prepending ?id=XYZ to the subscription criteria where XYZ is the id of the changed entity 308 * 309 * @since 3.6.1 310 */ 311 public void setEnableInMemorySubscriptionMatching(boolean theEnableInMemorySubscriptionMatching) { 312 myEnableInMemorySubscriptionMatching = theEnableInMemorySubscriptionMatching; 313 } 314 315 /** 316 * If set to {@link IndexEnabledEnum#DISABLED} (default is {@link IndexEnabledEnum#DISABLED}) 317 * the server will not create search indexes for search parameters with no values in resources. 318 * <p> 319 * Disabling this feature means that the <code>:missing</code> search modifier will not be 320 * supported on the server, but also means that storage and indexing (i.e. writes to the 321 * database) may be much faster on servers which have lots of search parameters and need 322 * to write quickly. 323 * </p> 324 * <p> 325 * This feature may be enabled on servers where supporting the use of the :missing parameter is 326 * of higher importance than raw write performance 327 * </p> 328 */ 329 public IndexEnabledEnum getIndexMissingFields() { 330 return myIndexMissingFieldsEnabled; 331 } 332 333 /** 334 * If set to {@link IndexEnabledEnum#DISABLED} (default is {@link IndexEnabledEnum#DISABLED}) 335 * the server will not create search indexes for search parameters with no values in resources. 336 * <p> 337 * Disabling this feature means that the <code>:missing</code> search modifier will not be 338 * supported on the server, but also means that storage and indexing (i.e. writes to the 339 * database) may be much faster on servers which have lots of search parameters and need 340 * to write quickly. 341 * </p> 342 * <p> 343 * This feature may be enabled on servers where supporting the use of the :missing parameter is 344 * of higher importance than raw write performance 345 * </p> 346 * <p> 347 * Note that this setting also has an impact on sorting (i.e. using the 348 * <code>_sort</code> parameter on searches): If the server is configured 349 * to not index missing field. 350 * </p> 351 * <p> 352 * The following index may need to be added into the indexed tables such as <code>HFJ_SPIDX_TOKEN</code> 353 * to improve the search performance while <code>:missing</code> is enabled. 354 * <code>RES_TYPE, SP_NAME, SP_MISSING</code> 355 * </p> 356 */ 357 public void setIndexMissingFields(IndexEnabledEnum theIndexMissingFields) { 358 Validate.notNull(theIndexMissingFields, "theIndexMissingFields must not be null"); 359 myIndexMissingFieldsEnabled = theIndexMissingFields; 360 } 361 362 /** 363 * If this is enabled (disabled by default), Mass Ingestion Mode is enabled. In this mode, a number of 364 * runtime checks are disabled. This mode is designed for rapid backloading of data while the system is not 365 * being otherwise used. 366 * <p> 367 * In this mode: 368 * <p> 369 * - Tags/Profiles/Security Labels will not be updated on existing resources that already have them 370 * - Resources modification checks will be skipped in favour of a simple hash check 371 * - Extra resource ID caching is enabled 372 * 373 * @since 5.5.0 374 */ 375 public boolean isMassIngestionMode() { 376 return myMassIngestionMode; 377 } 378 379 /** 380 * If this is enabled (disabled by default), Mass Ingestion Mode is enabled. In this mode, a number of 381 * runtime checks are disabled. This mode is designed for rapid backloading of data while the system is not 382 * being otherwise used. 383 * <p> 384 * In this mode: 385 * <p> 386 * - Tags/Profiles/Security Labels will not be updated on existing resources that already have them 387 * - Resources modification checks will be skipped in favour of a simple hash check 388 * - Extra resource ID caching is enabled 389 * 390 * @since 5.5.0 391 */ 392 public void setMassIngestionMode(boolean theMassIngestionMode) { 393 myMassIngestionMode = theMassIngestionMode; 394 } 395 396 /** 397 * Specifies the maximum number of resources permitted within a single transaction bundle. 398 * If a transaction bundle is submitted with more than this number of resources, it will be 399 * rejected with a PayloadTooLarge exception. 400 * <p> 401 * The default value is <code>null</code>, which means that there is no limit. 402 * </p> 403 */ 404 public Integer getMaximumTransactionBundleSize() { 405 return myMaximumTransactionBundleSize; 406 } 407 408 /** 409 * Specifies the maximum number of resources permitted within a single transaction bundle. 410 * If a transaction bundle is submitted with more than this number of resources, it will be 411 * rejected with a PayloadTooLarge exception. 412 * <p> 413 * The default value is <code>null</code>, which means that there is no limit. 414 * </p> 415 */ 416 public StorageSettings setMaximumTransactionBundleSize(Integer theMaximumTransactionBundleSize) { 417 myMaximumTransactionBundleSize = theMaximumTransactionBundleSize; 418 return this; 419 } 420 421 /** 422 * If set to true, attempt to map terminology for bulk export jobs using the 423 * logic in 424 * {@link ResponseTerminologyTranslationSvc}. Default is <code>false</code>. 425 * 426 * @since 6.3.0 427 */ 428 public boolean isNormalizeTerminologyForBulkExportJobs() { 429 return myNormalizeTerminologyForBulkExportJobs; 430 } 431 432 /** 433 * If set to true, attempt to map terminology for bulk export jobs using the 434 * logic in 435 * {@link ResponseTerminologyTranslationSvc}. Default is <code>false</code>. 436 * 437 * @since 6.3.0 438 */ 439 public void setNormalizeTerminologyForBulkExportJobs(boolean theNormalizeTerminologyForBulkExportJobs) { 440 myNormalizeTerminologyForBulkExportJobs = theNormalizeTerminologyForBulkExportJobs; 441 } 442 443 /** 444 * This is an internal API and may change or disappear without notice 445 * 446 * @since 6.2.0 447 */ 448 public Class<? extends ISequenceValueMassager> getSequenceValueMassagerClass() { 449 return mySequenceValueMassagerClass; 450 } 451 452 /** 453 * This is an internal API and may change or disappear without notice 454 * 455 * @since 6.2.0 456 */ 457 public void setSequenceValueMassagerClass(Class<? extends ISequenceValueMassager> theSequenceValueMassagerClass) { 458 Validate.notNull(theSequenceValueMassagerClass, "theSequenceValueMassagerClass must not be null"); 459 mySequenceValueMassagerClass = theSequenceValueMassagerClass; 460 } 461 462 /** 463 * If set to true (default is false) then subscriptions will be triggered for resource updates even if they 464 * do not trigger a new version (e.g. $meta-add and $meta-delete). 465 * 466 * @since 5.5.0 467 */ 468 public boolean isTriggerSubscriptionsForNonVersioningChanges() { 469 return myTriggerSubscriptionsForNonVersioningChanges; 470 } 471 472 /** 473 * If set to true (default is false) then subscriptions will be triggered for resource updates even if they 474 * do not trigger a new version (e.g. $meta-add and $meta-delete). 475 * 476 * @since 5.5.0 477 */ 478 public void setTriggerSubscriptionsForNonVersioningChanges(boolean theTriggerSubscriptionsForNonVersioningChanges) { 479 myTriggerSubscriptionsForNonVersioningChanges = theTriggerSubscriptionsForNonVersioningChanges; 480 } 481 482 /** 483 * If set to <code>true</code> (default is <code>false</code>) the 484 * <code>:of-type</code> modifier on token search parameters for 485 * identifiers will be supported. Enabling this causes additional 486 * indexing overhead (although very minor) so it is disabled unless it is 487 * actually needed. 488 * 489 * @since 5.7.0 490 */ 491 public boolean isIndexIdentifierOfType() { 492 return myIndexIdentifierOfType; 493 } 494 495 /** 496 * If set to <code>true</code> (default is <code>false</code>) the 497 * <code>:of-type</code> modifier on token search parameters for 498 * identifiers will be supported. Enabling this causes additional 499 * indexing overhead (although very minor) so it is disabled unless it is 500 * actually needed. 501 * 502 * @since 5.7.0 503 */ 504 public void setIndexIdentifierOfType(boolean theIndexIdentifierOfType) { 505 myIndexIdentifierOfType = theIndexIdentifierOfType; 506 } 507 508 /** 509 * If set to {@code true} the default search params (i.e. the search parameters that are 510 * defined by the FHIR specification itself) may be overridden by uploading search 511 * parameters to the server with the same code as the built-in search parameter. 512 * <p> 513 * This can be useful if you want to be able to disable or alter 514 * the behaviour of the default search parameters. 515 * </p> 516 * <p> 517 * The default value for this setting is {@code true} 518 * </p> 519 */ 520 public boolean isDefaultSearchParamsCanBeOverridden() { 521 return myDefaultSearchParamsCanBeOverridden; 522 } 523 524 /** 525 * If set to {@code true} the default search params (i.e. the search parameters that are 526 * defined by the FHIR specification itself) may be overridden by uploading search 527 * parameters to the server with the same code as the built-in search parameter. 528 * <p> 529 * This can be useful if you want to be able to disable or alter 530 * the behaviour of the default search parameters. 531 * </p> 532 * <p> 533 * The default value for this setting is {@code true} 534 * </p> 535 */ 536 public void setDefaultSearchParamsCanBeOverridden(boolean theDefaultSearchParamsCanBeOverridden) { 537 myDefaultSearchParamsCanBeOverridden = theDefaultSearchParamsCanBeOverridden; 538 } 539 540 /** 541 * If enabled, the server will support the use of :contains searches, 542 * which are helpful but can have adverse effects on performance. 543 * <p> 544 * Default is <code>false</code> (Note that prior to HAPI FHIR 545 * 3.5.0 the default was <code>true</code>) 546 * </p> 547 * <p> 548 * Note: If you change this value after data already has 549 * already been stored in the database, you must for a reindexing 550 * of all data in the database or resources may not be 551 * searchable. 552 * </p> 553 */ 554 public boolean isAllowContainsSearches() { 555 return myAllowContainsSearches; 556 } 557 558 /** 559 * If enabled, the server will support the use of :contains searches, 560 * which are helpful but can have adverse effects on performance. 561 * <p> 562 * Default is <code>false</code> (Note that prior to HAPI FHIR 563 * 3.5.0 the default was <code>true</code>) 564 * </p> 565 * <p> 566 * Note: If you change this value after data already has 567 * already been stored in the database, you must for a reindexing 568 * of all data in the database or resources may not be 569 * searchable. 570 * </p> 571 */ 572 public void setAllowContainsSearches(boolean theAllowContainsSearches) { 573 this.myAllowContainsSearches = theAllowContainsSearches; 574 } 575 576 /** 577 * If enabled, the server will support the use of :mdm search parameter qualifier on Reference Search Parameters. 578 * This Parameter Qualifier is HAPI-specific, and not defined anywhere in the FHIR specification. Using this qualifier 579 * will result in an MDM expansion being done on the reference, which will expand the search scope. For example, if Patient/1 580 * is MDM-matched to Patient/2 and you execute the search: 581 * Observation?subject:mdm=Patient/1 , you will receive observations for both Patient/1 and Patient/2. 582 * <p> 583 * Default is <code>false</code> 584 * </p> 585 * 586 * @since 5.4.0 587 */ 588 public boolean isAllowMdmExpansion() { 589 return myAllowMdmExpansion; 590 } 591 592 /** 593 * If enabled, the server will support the use of :mdm search parameter qualifier on Reference Search Parameters. 594 * This Parameter Qualifier is HAPI-specific, and not defined anywhere in the FHIR specification. Using this qualifier 595 * will result in an MDM expansion being done on the reference, which will expand the search scope. For example, if Patient/1 596 * is MDM-matched to Patient/2 and you execute the search: 597 * Observation?subject:mdm=Patient/1 , you will receive observations for both Patient/1 and Patient/2. 598 * <p> 599 * Default is <code>false</code> 600 * </p> 601 * 602 * @since 5.4.0 603 */ 604 public void setAllowMdmExpansion(boolean theAllowMdmExpansion) { 605 myAllowMdmExpansion = theAllowMdmExpansion; 606 } 607 608 /** 609 * If set to <code>true</code> (default is <code>false</code>) the server will allow 610 * resources to have references to external servers. For example if this server is 611 * running at <code>http://example.com/fhir</code> and this setting is set to 612 * <code>true</code> the server will allow a Patient resource to be saved with a 613 * Patient.organization value of <code>http://foo.com/Organization/1</code>. 614 * <p> 615 * Under the default behaviour if this value has not been changed, the above 616 * resource would be rejected by the server because it requires all references 617 * to be resolvable on the local server. 618 * </p> 619 * <p> 620 * Note that external references will be indexed by the server and may be searched 621 * (e.g. <code>Patient:organization</code>), but 622 * chained searches (e.g. <code>Patient:organization.name</code>) will not work across 623 * these references. 624 * </p> 625 * <p> 626 * It is recommended to also set {@link #setTreatBaseUrlsAsLocal(Set)} if this value 627 * is set to <code>true</code> 628 * </p> 629 * 630 * @see #setTreatBaseUrlsAsLocal(Set) 631 * @see #setAllowExternalReferences(boolean) 632 */ 633 public boolean isAllowExternalReferences() { 634 return myAllowExternalReferences; 635 } 636 637 /** 638 * If set to <code>true</code> (default is <code>false</code>) the server will allow 639 * resources to have references to external servers. For example if this server is 640 * running at <code>http://example.com/fhir</code> and this setting is set to 641 * <code>true</code> the server will allow a Patient resource to be saved with a 642 * Patient.organization value of <code>http://foo.com/Organization/1</code>. 643 * <p> 644 * Under the default behaviour if this value has not been changed, the above 645 * resource would be rejected by the server because it requires all references 646 * to be resolvable on the local server. 647 * </p> 648 * <p> 649 * Note that external references will be indexed by the server and may be searched 650 * (e.g. <code>Patient:organization</code>), but 651 * chained searches (e.g. <code>Patient:organization.name</code>) will not work across 652 * these references. 653 * </p> 654 * <p> 655 * It is recommended to also set {@link #setTreatBaseUrlsAsLocal(Set)} if this value 656 * is set to <code>true</code> 657 * </p> 658 * 659 * @see #setTreatBaseUrlsAsLocal(Set) 660 * @see #setAllowExternalReferences(boolean) 661 */ 662 public void setAllowExternalReferences(boolean theAllowExternalReferences) { 663 myAllowExternalReferences = theAllowExternalReferences; 664 } 665 666 /** 667 * This setting may be used to advise the server that any references found in 668 * resources that have any of the base URLs given here will be replaced with 669 * simple local references. 670 * <p> 671 * For example, if the set contains the value <code>http://example.com/base/</code> 672 * and a resource is submitted to the server that contains a reference to 673 * <code>http://example.com/base/Patient/1</code>, the server will automatically 674 * convert this reference to <code>Patient/1</code> 675 * </p> 676 * <p> 677 * Note that this property has different behaviour from {@link StorageSettings#getTreatReferencesAsLogical()} 678 * </p> 679 * 680 * @see #getTreatReferencesAsLogical() 681 */ 682 public Set<String> getTreatBaseUrlsAsLocal() { 683 return myTreatBaseUrlsAsLocal; 684 } 685 686 /** 687 * This setting may be used to advise the server that any references found in 688 * resources that have any of the base URLs given here will be replaced with 689 * simple local references. 690 * <p> 691 * For example, if the set contains the value <code>http://example.com/base/</code> 692 * and a resource is submitted to the server that contains a reference to 693 * <code>http://example.com/base/Patient/1</code>, the server will automatically 694 * convert this reference to <code>Patient/1</code> 695 * </p> 696 * 697 * @param theTreatBaseUrlsAsLocal The set of base URLs. May be <code>null</code>, which 698 * means no references will be treated as external 699 */ 700 public void setTreatBaseUrlsAsLocal(Set<String> theTreatBaseUrlsAsLocal) { 701 if (theTreatBaseUrlsAsLocal != null) { 702 for (String next : theTreatBaseUrlsAsLocal) { 703 validateTreatBaseUrlsAsLocal(next); 704 } 705 } 706 707 HashSet<String> treatBaseUrlsAsLocal = new HashSet<>(); 708 for (String next : defaultIfNull(theTreatBaseUrlsAsLocal, new HashSet<String>())) { 709 while (next.endsWith("/")) { 710 next = next.substring(0, next.length() - 1); 711 } 712 treatBaseUrlsAsLocal.add(next); 713 } 714 myTreatBaseUrlsAsLocal = treatBaseUrlsAsLocal; 715 } 716 717 /** 718 * Add a value to the {@link #setTreatReferencesAsLogical(Set) logical references list}. 719 * 720 * @see #setTreatReferencesAsLogical(Set) 721 */ 722 public void addTreatReferencesAsLogical(String theTreatReferencesAsLogical) { 723 validateTreatBaseUrlsAsLocal(theTreatReferencesAsLogical); 724 725 if (myTreatReferencesAsLogical == null) { 726 myTreatReferencesAsLogical = new HashSet<>(); 727 } 728 myTreatReferencesAsLogical.add(theTreatReferencesAsLogical); 729 } 730 731 /** 732 * This setting may be used to advise the server that any references found in 733 * resources that have any of the base URLs given here will be treated as logical 734 * references instead of being treated as real references. 735 * <p> 736 * A logical reference is a reference which is treated as an identifier, and 737 * does not neccesarily resolve. See <a href="http://hl7.org/fhir/references.html">references</a> for 738 * a description of logical references. For example, the valueset 739 * <a href="http://hl7.org/fhir/valueset-quantity-comparator.html">valueset-quantity-comparator</a> is a logical 740 * reference. 741 * </p> 742 * <p> 743 * Values for this field may take either of the following forms: 744 * </p> 745 * <ul> 746 * <li><code>http://example.com/some-url</code> <b>(will be matched exactly)</b></li> 747 * <li><code>http://example.com/some-base*</code> <b>(will match anything beginning with the part before the *)</b></li> 748 * </ul> 749 * 750 * @see #DEFAULT_LOGICAL_BASE_URLS Default values for this property 751 */ 752 public Set<String> getTreatReferencesAsLogical() { 753 return myTreatReferencesAsLogical; 754 } 755 756 /** 757 * This setting may be used to advise the server that any references found in 758 * resources that have any of the base URLs given here will be treated as logical 759 * references instead of being treated as real references. 760 * <p> 761 * A logical reference is a reference which is treated as an identifier, and 762 * does not neccesarily resolve. See <a href="http://hl7.org/fhir/references.html">references</a> for 763 * a description of logical references. For example, the valueset 764 * <a href="http://hl7.org/fhir/valueset-quantity-comparator.html">valueset-quantity-comparator</a> is a logical 765 * reference. 766 * </p> 767 * <p> 768 * Values for this field may take either of the following forms: 769 * </p> 770 * <ul> 771 * <li><code>http://example.com/some-url</code> <b>(will be matched exactly)</b></li> 772 * <li><code>http://example.com/some-base*</code> <b>(will match anything beginning with the part before the *)</b></li> 773 * </ul> 774 * 775 * @see #DEFAULT_LOGICAL_BASE_URLS Default values for this property 776 */ 777 public StorageSettings setTreatReferencesAsLogical(Set<String> theTreatReferencesAsLogical) { 778 myTreatReferencesAsLogical = theTreatReferencesAsLogical; 779 return this; 780 } 781 782 /** 783 * This setting indicates which subscription channel types are supported by the server. Any subscriptions submitted 784 * to the server matching these types will be activated. 785 */ 786 public StorageSettings addSupportedSubscriptionType( 787 Subscription.SubscriptionChannelType theSubscriptionChannelType) { 788 mySupportedSubscriptionTypes.add(theSubscriptionChannelType); 789 return this; 790 } 791 792 /** 793 * This setting indicates which subscription channel types are supported by the server. Any subscriptions submitted 794 * to the server matching these types will be activated. 795 */ 796 public Set<Subscription.SubscriptionChannelType> getSupportedSubscriptionTypes() { 797 return Collections.unmodifiableSet(mySupportedSubscriptionTypes); 798 } 799 800 /** 801 * Indicate whether a subscription channel type is supported by this server. 802 * 803 * @return true if at least one subscription channel type is supported by this server false otherwise. 804 */ 805 public boolean hasSupportedSubscriptionTypes() { 806 return CollectionUtils.isNotEmpty(mySupportedSubscriptionTypes); 807 } 808 809 @VisibleForTesting 810 public void clearSupportedSubscriptionTypesForUnitTest() { 811 mySupportedSubscriptionTypes.clear(); 812 } 813 814 /** 815 * If e-mail subscriptions are supported, the From address used when sending e-mails 816 */ 817 public String getEmailFromAddress() { 818 return myEmailFromAddress; 819 } 820 821 /** 822 * If e-mail subscriptions are supported, the From address used when sending e-mails 823 */ 824 public void setEmailFromAddress(String theEmailFromAddress) { 825 myEmailFromAddress = theEmailFromAddress; 826 } 827 828 /** 829 * If websocket subscriptions are enabled, this specifies the context path that listens to them. Default value "/websocket". 830 */ 831 public String getWebsocketContextPath() { 832 return myWebsocketContextPath; 833 } 834 835 /** 836 * If websocket subscriptions are enabled, this specifies the context path that listens to them. Default value "/websocket". 837 */ 838 public void setWebsocketContextPath(String theWebsocketContextPath) { 839 myWebsocketContextPath = theWebsocketContextPath; 840 } 841 842 /** 843 * <p> 844 * Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in 845 * {@link ResourceIndexedSearchParamDate} when resolving searches where all predicates are using 846 * precision of {@link TemporalPrecisionEnum#DAY}. 847 * <p> 848 * For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an 849 * integer representing the ordinal date {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()} 850 * and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()} 851 * </p> 852 * Default is {@literal true} beginning in HAPI FHIR 5.0.0 853 * </p> 854 * 855 * @since 5.0.0 856 */ 857 public boolean getUseOrdinalDatesForDayPrecisionSearches() { 858 return myUseOrdinalDatesForDayPrecisionSearches; 859 } 860 861 /** 862 * <p> 863 * Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in 864 * {@link ResourceIndexedSearchParamDate} when resolving searches where all predicates are using 865 * precision of {@link TemporalPrecisionEnum#DAY}. 866 * <p> 867 * For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an 868 * ordinal {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()} 869 * and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()} 870 * </p> 871 * Default is {@literal true} beginning in HAPI FHIR 5.0.0 872 * </p> 873 * 874 * @since 5.0.0 875 */ 876 public void setUseOrdinalDatesForDayPrecisionSearches(boolean theUseOrdinalDates) { 877 myUseOrdinalDatesForDayPrecisionSearches = theUseOrdinalDates; 878 } 879 880 /** 881 * If set to <code>true</code> (default is <code>false</code>), when indexing SearchParameter values for token SearchParameter, 882 * the string component to support the <code>:text</code> modifier will be disabled. This means that the following fields 883 * will not be indexed for tokens: 884 * <ul> 885 * <li>CodeableConcept.text</li> 886 * <li>Coding.display</li> 887 * <li>Identifier.use.text</li> 888 * </ul> 889 * 890 * @since 5.0.0 891 */ 892 public boolean isSuppressStringIndexingInTokens() { 893 return mySuppressStringIndexingInTokens; 894 } 895 896 /** 897 * If set to <code>true</code> (default is <code>false</code>), when indexing SearchParameter values for token SearchParameter, 898 * the string component to support the <code>:text</code> modifier will be disabled. This means that the following fields 899 * will not be indexed for tokens: 900 * <ul> 901 * <li>CodeableConcept.text</li> 902 * <li>Coding.display</li> 903 * <li>Identifier.use.text</li> 904 * </ul> 905 * 906 * @since 5.0.0 907 */ 908 public void setSuppressStringIndexingInTokens(boolean theSuppressStringIndexingInTokens) { 909 mySuppressStringIndexingInTokens = theSuppressStringIndexingInTokens; 910 } 911 912 /** 913 * When indexing a Period (e.g. Encounter.period) where the period has an upper bound 914 * but not a lower bound, a canned "start of time" value can be used as the lower bound 915 * in order to allow range searches to correctly identify all values in the range. 916 * <p> 917 * The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which 918 * is probably good enough for almost any application, but this can be changed if 919 * needed. 920 * </p> 921 * <p> 922 * Note the following database documented limitations: 923 * <ul> 924 * <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li> 925 * <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li> 926 * <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li> 927 * <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li> 928 * <li>H2: datetime2 Low Value -4713 and High Value 9999</li> 929 * </ul> 930 * </p> 931 * 932 * @see #getPeriodIndexEndOfTime() 933 * @since 5.1.0 934 */ 935 public IPrimitiveType<Date> getPeriodIndexStartOfTime() { 936 return myPeriodIndexStartOfTime; 937 } 938 939 /** 940 * When indexing a Period (e.g. Encounter.period) where the period has an upper bound 941 * but not a lower bound, a canned "start of time" value can be used as the lower bound 942 * in order to allow range searches to correctly identify all values in the range. 943 * <p> 944 * The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which 945 * is probably good enough for almost any application, but this can be changed if 946 * needed. 947 * </p> 948 * <p> 949 * Note the following database documented limitations: 950 * <ul> 951 * <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li> 952 * <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li> 953 * <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li> 954 * <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li> 955 * <li>H2: datetime2 Low Value -4713 and High Value 9999</li> 956 * </ul> 957 * </p> 958 * 959 * @see #getPeriodIndexEndOfTime() 960 * @since 5.1.0 961 */ 962 public void setPeriodIndexStartOfTime(IPrimitiveType<Date> thePeriodIndexStartOfTime) { 963 Validate.notNull(thePeriodIndexStartOfTime, "thePeriodIndexStartOfTime must not be null"); 964 myPeriodIndexStartOfTime = thePeriodIndexStartOfTime; 965 } 966 967 /** 968 * When indexing a Period (e.g. Encounter.period) where the period has a lower bound 969 * but not an upper bound, a canned "end of time" value can be used as the upper bound 970 * in order to allow range searches to correctly identify all values in the range. 971 * <p> 972 * The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which 973 * is probably good enough for almost any application, but this can be changed if 974 * needed. 975 * </p> 976 * <p> 977 * Note the following database documented limitations: 978 * <ul> 979 * <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li> 980 * <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li> 981 * <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li> 982 * <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li> 983 * <li>H2: datetime2 Low Value -4713 and High Value 9999</li> 984 * </ul> 985 * </p> 986 * 987 * @see #getPeriodIndexStartOfTime() 988 * @since 5.1.0 989 */ 990 public IPrimitiveType<Date> getPeriodIndexEndOfTime() { 991 return myPeriodIndexEndOfTime; 992 } 993 994 /** 995 * When indexing a Period (e.g. Encounter.period) where the period has an upper bound 996 * but not a lower bound, a canned "start of time" value can be used as the lower bound 997 * in order to allow range searches to correctly identify all values in the range. 998 * <p> 999 * The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which 1000 * is probably good enough for almost any application, but this can be changed if 1001 * needed. 1002 * </p> 1003 * <p> 1004 * Note the following database documented limitations: 1005 * <ul> 1006 * <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li> 1007 * <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li> 1008 * <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li> 1009 * <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li> 1010 * <li>H2: datetime2 Low Value -4713 and High Value 9999</li> 1011 * </ul> 1012 * </p> 1013 * 1014 * @see #getPeriodIndexStartOfTime() 1015 * @since 5.1.0 1016 */ 1017 public void setPeriodIndexEndOfTime(IPrimitiveType<Date> thePeriodIndexEndOfTime) { 1018 Validate.notNull(thePeriodIndexEndOfTime, "thePeriodIndexEndOfTime must not be null"); 1019 myPeriodIndexEndOfTime = thePeriodIndexEndOfTime; 1020 } 1021 1022 /** 1023 * Toggles whether Quantity searches support value normalization when using valid UCUM coded values. 1024 * 1025 * <p> 1026 * The default value is {@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED} which is current behavior. 1027 * </p> 1028 * <p> 1029 * Here is the UCUM service support level 1030 * <ul> 1031 * <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED}, default, Quantity is stored in {@link ResourceIndexedSearchParamQuantity} only and it is used by searching.</li> 1032 * <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_STORAGE_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, but {@link ResourceIndexedSearchParamQuantity} is used by searching.</li> 1033 * <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, {@link ResourceIndexedSearchParamQuantityNormalized} is used by searching.</li> 1034 * </ul> 1035 * </p> 1036 * 1037 * @since 5.3.0 1038 */ 1039 public NormalizedQuantitySearchLevel getNormalizedQuantitySearchLevel() { 1040 return myNormalizedQuantitySearchLevel; 1041 } 1042 1043 /** 1044 * Toggles whether Quantity searches support value normalization when using valid UCUM coded values. 1045 * 1046 * <p> 1047 * The default value is {@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED} which is current behavior. 1048 * </p> 1049 * <p> 1050 * Here is the UCUM service support level 1051 * <ul> 1052 * <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED}, default, Quantity is stored in {@link ResourceIndexedSearchParamQuantity} only and it is used by searching.</li> 1053 * <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_STORAGE_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, but {@link ResourceIndexedSearchParamQuantity} is used by searching.</li> 1054 * <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, {@link ResourceIndexedSearchParamQuantityNormalized} is used by searching.</li> 1055 * </ul> 1056 * </p> 1057 * 1058 * @since 5.3.0 1059 */ 1060 public void setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel theNormalizedQuantitySearchLevel) { 1061 myNormalizedQuantitySearchLevel = theNormalizedQuantitySearchLevel; 1062 } 1063 1064 /** 1065 * When set with resource paths (e.g. <code>"Observation.subject"</code>), any references found at the given paths 1066 * will automatically have versions appended. The version used will be the current version of the given resource. 1067 * 1068 * @since 5.3.0 1069 */ 1070 public Set<String> getAutoVersionReferenceAtPaths() { 1071 return myAutoVersionReferenceAtPaths; 1072 } 1073 1074 /** 1075 * When set with resource paths (e.g. <code>"Observation.subject"</code>), any references found at the given paths 1076 * will automatically have versions appended. The version used will be the current version of the given resource. 1077 * <p> 1078 * Versions will only be added if the reference does not already have a version, so any versioned references 1079 * supplied by the client will take precedence over the automatic current version. 1080 * </p> 1081 * <p> 1082 * Note that for this setting to be useful, the {@link ParserOptions} 1083 * {@link ParserOptions#getDontStripVersionsFromReferencesAtPaths() DontStripVersionsFromReferencesAtPaths} 1084 * option must also be set. 1085 * </p> 1086 * 1087 * @param thePaths A collection of reference paths for which the versions will be appended automatically 1088 * when serializing, e.g. "Patient.managingOrganization" or "AuditEvent.object.reference". Note that 1089 * only resource name and field names with dots separating is allowed here (no repetition 1090 * indicators, FluentPath expressions, etc.) 1091 * @since 5.3.0 1092 */ 1093 public void setAutoVersionReferenceAtPaths(String... thePaths) { 1094 Set<String> paths = Collections.emptySet(); 1095 if (thePaths != null) { 1096 paths = new HashSet<>(Arrays.asList(thePaths)); 1097 } 1098 setAutoVersionReferenceAtPaths(paths); 1099 } 1100 1101 /** 1102 * When set with resource paths (e.g. <code>"Observation.subject"</code>), any references found at the given paths 1103 * will automatically have versions appended. The version used will be the current version of the given resource. 1104 * <p> 1105 * Versions will only be added if the reference does not already have a version, so any versioned references 1106 * supplied by the client will take precedence over the automatic current version. 1107 * </p> 1108 * <p> 1109 * Note that for this setting to be useful, the {@link ParserOptions} 1110 * {@link ParserOptions#getDontStripVersionsFromReferencesAtPaths() DontStripVersionsFromReferencesAtPaths} 1111 * option must also be set 1112 * </p> 1113 * 1114 * @param thePaths A collection of reference paths for which the versions will be appended automatically 1115 * when serializing, e.g. "Patient.managingOrganization" or "AuditEvent.object.reference". Note that 1116 * only resource name and field names with dots separating is allowed here (no repetition 1117 * indicators, FluentPath expressions, etc.) 1118 * @since 5.3.0 1119 */ 1120 public void setAutoVersionReferenceAtPaths(Set<String> thePaths) { 1121 Set<String> paths = defaultIfNull(thePaths, Collections.emptySet()); 1122 Map<String, Set<String>> byType = new HashMap<>(); 1123 for (String nextPath : paths) { 1124 int doxIdx = nextPath.indexOf('.'); 1125 Validate.isTrue(doxIdx > 0, "Invalid path for auto-version reference at path: %s", nextPath); 1126 String type = nextPath.substring(0, doxIdx); 1127 byType.computeIfAbsent(type, t -> new HashSet<>()).add(nextPath); 1128 } 1129 1130 myAutoVersionReferenceAtPaths = paths; 1131 myTypeToAutoVersionReferenceAtPaths = byType; 1132 } 1133 1134 /** 1135 * Returns a sub-collection of {@link #getAutoVersionReferenceAtPaths()} containing only paths 1136 * for the given resource type. 1137 * 1138 * @since 5.3.0 1139 */ 1140 public Set<String> getAutoVersionReferenceAtPathsByResourceType(String theResourceType) { 1141 Validate.notEmpty(theResourceType, "theResourceType must not be null or empty"); 1142 Set<String> retVal = myTypeToAutoVersionReferenceAtPaths.get(theResourceType); 1143 retVal = defaultIfNull(retVal, Collections.emptySet()); 1144 return retVal; 1145 } 1146 1147 /** 1148 * Should searches with <code>_include</code> respect versioned references, and pull the specific requested version. 1149 * This may have performance impacts on heavily loaded systems. 1150 * 1151 * @since 5.3.0 1152 */ 1153 public boolean isRespectVersionsForSearchIncludes() { 1154 return myRespectVersionsForSearchIncludes; 1155 } 1156 1157 /** 1158 * Should searches with <code>_include</code> respect versioned references, and pull the specific requested version. 1159 * This may have performance impacts on heavily loaded systems. 1160 * 1161 * @since 5.3.0 1162 */ 1163 public void setRespectVersionsForSearchIncludes(boolean theRespectVersionsForSearchIncludes) { 1164 myRespectVersionsForSearchIncludes = theRespectVersionsForSearchIncludes; 1165 } 1166 1167 /** 1168 * If enabled, "Uplifted Refchains" will be enabled. This feature causes 1169 * HAPI FHIR to generate indexes for stored resources that include the current 1170 * value of the target of a chained reference, such as "Encounter?subject.name". 1171 * 1172 * @since 6.6.0 1173 */ 1174 public boolean isIndexOnUpliftedRefchains() { 1175 return myIndexOnUpliftedRefchains; 1176 } 1177 1178 /** 1179 * If enabled, "Uplifted Refchains" will be enabled. This feature causes 1180 * HAPI FHIR to generate indexes for stored resources that include the current 1181 * value of the target of a chained reference, such as "Encounter?subject.name". 1182 * 1183 * @since 6.6.0 1184 */ 1185 public void setIndexOnUpliftedRefchains(boolean theIndexOnUpliftedRefchains) { 1186 myIndexOnUpliftedRefchains = theIndexOnUpliftedRefchains; 1187 } 1188 1189 /** 1190 * Should indexing and searching on contained resources be enabled on this server. 1191 * This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>. 1192 * 1193 * @since 5.4.0 1194 */ 1195 public boolean isIndexOnContainedResources() { 1196 return myIndexOnContainedResources; 1197 } 1198 1199 /** 1200 * Should indexing and searching on contained resources be enabled on this server. 1201 * This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>. 1202 * 1203 * @since 5.4.0 1204 */ 1205 public void setIndexOnContainedResources(boolean theIndexOnContainedResources) { 1206 myIndexOnContainedResources = theIndexOnContainedResources; 1207 } 1208 1209 /** 1210 * Should recursive indexing and searching on contained resources be enabled on this server. 1211 * This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>. 1212 * 1213 * @since 5.6.0 1214 */ 1215 public boolean isIndexOnContainedResourcesRecursively() { 1216 return myIndexOnContainedResourcesRecursively; 1217 } 1218 1219 /** 1220 * Should recursive indexing and searching on contained resources be enabled on this server. 1221 * This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>. 1222 * 1223 * @since 5.6.0 1224 */ 1225 public void setIndexOnContainedResourcesRecursively(boolean theIndexOnContainedResourcesRecursively) { 1226 myIndexOnContainedResourcesRecursively = theIndexOnContainedResourcesRecursively; 1227 } 1228 1229 /** 1230 * If this is disabled by setting this to {@literal false} (default is {@literal true}), 1231 * the server will not automatically implement and support search parameters that 1232 * are not explcitly created in the repository. 1233 * <p> 1234 * Disabling this can have a dramatic improvement on performance (especially write performance) 1235 * in servers that only need to support a small number of search parameters, or no search parameters at all. 1236 * Disabling this obviously reduces the options for searching however. 1237 * </p> 1238 * 1239 * @since 5.7.0 1240 */ 1241 public boolean isAutoSupportDefaultSearchParams() { 1242 return myAutoSupportDefaultSearchParams; 1243 } 1244 1245 /** 1246 * If this is disabled by setting this to {@literal false} (default is {@literal true}), 1247 * the server will not automatically implement and support search parameters that 1248 * are not explcitly created in the repository. 1249 * <p> 1250 * Disabling this can have a dramatic improvement on performance (especially write performance) 1251 * in servers that only need to support a small number of search parameters, or no search parameters at all. 1252 * Disabling this obviously reduces the options for searching however. 1253 * </p> 1254 * 1255 * @since 5.7.0 1256 */ 1257 public void setAutoSupportDefaultSearchParams(boolean theAutoSupportDefaultSearchParams) { 1258 myAutoSupportDefaultSearchParams = theAutoSupportDefaultSearchParams; 1259 } 1260 1261 /** 1262 * If enabled, the server will support cross-partition subscription. 1263 * This subscription will be the responsible for all the requests from all the partitions on this server. 1264 * For example, if the server has 3 partitions, P1, P2, P3 1265 * The subscription will live in the DEFAULT partition. Resource posted to DEFAULT, P1, P2, and P3 will trigger this subscription. 1266 * <p> 1267 * Default is <code>false</code> 1268 * </p> 1269 * 1270 * @since 5.7.0 1271 */ 1272 public boolean isCrossPartitionSubscriptionEnabled() { 1273 return myCrossPartitionSubscriptionEnabled; 1274 } 1275 1276 /** 1277 * If enabled, the server will support cross-partition subscription. 1278 * This subscription will be the responsible for all the requests from all the partitions on this server. 1279 * For example, if the server has 3 partitions, P1, P2, P3 1280 * The subscription will live in the DEFAULT partition. Resource posted to DEFAULT, P1, P2, and P3 will trigger this subscription. 1281 * <p> 1282 * Default is <code>false</code> 1283 * </p> 1284 * 1285 * @since 5.7.0 1286 */ 1287 public void setCrossPartitionSubscriptionEnabled(boolean theAllowCrossPartitionSubscription) { 1288 myCrossPartitionSubscriptionEnabled = theAllowCrossPartitionSubscription; 1289 } 1290 1291 /** 1292 * This setting controls whether the {@link BaseChannelSettings#isQualifyChannelName} 1293 * should be qualified or not. 1294 * Default is true, ie, the channel name will be qualified. 1295 * 1296 * @since 6.4.0 1297 */ 1298 public void setQualifySubscriptionMatchingChannelName(boolean theQualifySubscriptionMatchingChannelName) { 1299 myQualifySubscriptionMatchingChannelName = theQualifySubscriptionMatchingChannelName; 1300 } 1301 1302 /** 1303 * This setting return whether the {@link BaseChannelSettings#isQualifyChannelName} 1304 * should be qualified or not. 1305 * 1306 * @return whether the {@link BaseChannelSettings#isQualifyChannelName} is qualified or not 1307 * @since 6.4.0 1308 */ 1309 public boolean isQualifySubscriptionMatchingChannelName() { 1310 return myQualifySubscriptionMatchingChannelName; 1311 } 1312 1313 /** 1314 * @return Should the {@literal _lamguage} SearchParameter be supported on this server? Defaults to {@literal false}. 1315 * @since 7.0.0 1316 */ 1317 public boolean isLanguageSearchParameterEnabled() { 1318 return myLanguageSearchParameterEnabled; 1319 } 1320 1321 /** 1322 * Should the {@literal _lamguage} SearchParameter be supported on this server? Defaults to {@literal false}. 1323 * 1324 * @since 7.0.0 1325 */ 1326 public void setLanguageSearchParameterEnabled(boolean theLanguageSearchParameterEnabled) { 1327 myLanguageSearchParameterEnabled = theLanguageSearchParameterEnabled; 1328 } 1329 1330 /** 1331 * @return true if the filter is enabled for resources installed via package installer, false otherwise 1332 * @since 7.0.0 1333 */ 1334 public boolean isValidateResourceStatusForPackageUpload() { 1335 return myValidateResourceStatusForPackageUpload; 1336 } 1337 1338 /** 1339 * Should resources being installed via package installer be filtered. 1340 * @since 7.0.0 1341 */ 1342 public void setValidateResourceStatusForPackageUpload(boolean theValidateResourceStatusForPackageUpload) { 1343 myValidateResourceStatusForPackageUpload = theValidateResourceStatusForPackageUpload; 1344 } 1345 1346 private static void validateTreatBaseUrlsAsLocal(String theUrl) { 1347 Validate.notBlank(theUrl, "Base URL must not be null or empty"); 1348 1349 int starIdx = theUrl.indexOf('*'); 1350 if (starIdx != -1) { 1351 if (starIdx != theUrl.length() - 1) { 1352 throw new IllegalArgumentException(Msg.code(1525) 1353 + "Base URL wildcard character (*) can only appear at the end of the string: " + theUrl); 1354 } 1355 } 1356 } 1357 1358 public enum IndexEnabledEnum { 1359 ENABLED, 1360 DISABLED 1361 } 1362}