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