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}