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.config; 021 022import com.google.common.annotations.VisibleForTesting; 023import org.apache.commons.collections4.CollectionUtils; 024import org.hl7.fhir.dstu2.model.Subscription; 025 026import java.util.Collections; 027import java.util.HashSet; 028import java.util.Set; 029 030import static org.apache.commons.lang3.StringUtils.isNotBlank; 031 032public abstract class BaseSubscriptionSettings { 033 public static final String DEFAULT_EMAIL_FROM_ADDRESS = "noreply@unknown.com"; 034 public static final String DEFAULT_WEBSOCKET_CONTEXT_PATH = "/websocket"; 035 public static final String DEFAULT_RESTHOOK_ENDPOINTURL_VALIDATION_REGEX = 036 "((((http?|https?)://))([-%()_.!~*';/?:@&=+$,A-Za-z0-9])+)"; 037 public static final long DEFAULT_SUBMISSION_INTERVAL_IN_MS = 5000; 038 039 private final Set<Subscription.SubscriptionChannelType> mySupportedSubscriptionTypes = new HashSet<>(); 040 private String myEmailFromAddress = DEFAULT_EMAIL_FROM_ADDRESS; 041 private String myWebsocketContextPath = DEFAULT_WEBSOCKET_CONTEXT_PATH; 042 private boolean myQualifySubscriptionMatchingChannelName = true; 043 private boolean myCrossPartitionSubscriptionEnabled = true; 044 private boolean myEnableInMemorySubscriptionMatching = true; 045 private boolean myTriggerSubscriptionsForNonVersioningChanges; 046 private long mySubmissionIntervalInMs = DEFAULT_SUBMISSION_INTERVAL_IN_MS; 047 048 /** 049 * @since 6.8.0 050 * Prevents any non IN-MEMORY Search params from being created by users. 051 */ 052 private boolean myAllowOnlyInMemorySubscriptions = false; 053 054 /** 055 * If this is enabled (default is {@literal false}), changes to Subscription resource would be put on queue immediately. 056 * Reducing delay between creation of the Subscription and Activation. 057 * 058 * @since 7.8.0 059 */ 060 private boolean mySubscriptionChangeQueuedImmediately = false; 061 062 /** 063 * @since 7.8.0 064 * 065 * Regex To perform validation on the endpoint URL for Subscription of type RESTHOOK. 066 */ 067 private String myRestHookEndpointUrlValidationRegex = DEFAULT_RESTHOOK_ENDPOINTURL_VALIDATION_REGEX; 068 069 /** 070 * This setting indicates which subscription channel types are supported by the server. Any subscriptions submitted 071 * to the server matching these types will be activated. 072 */ 073 public void addSupportedSubscriptionType(Subscription.SubscriptionChannelType theSubscriptionChannelType) { 074 mySupportedSubscriptionTypes.add(theSubscriptionChannelType); 075 } 076 077 /** 078 * This setting indicates which subscription channel types are supported by the server. Any subscriptions submitted 079 * to the server matching these types will be activated. 080 */ 081 public Set<Subscription.SubscriptionChannelType> getSupportedSubscriptionTypes() { 082 return Collections.unmodifiableSet(mySupportedSubscriptionTypes); 083 } 084 085 /** 086 * Indicate whether a subscription channel type is supported by this server. 087 * 088 * @return true if at least one subscription channel type is supported by this server false otherwise. 089 */ 090 public boolean hasSupportedSubscriptionTypes() { 091 return CollectionUtils.isNotEmpty(mySupportedSubscriptionTypes); 092 } 093 094 @VisibleForTesting 095 public void clearSupportedSubscriptionTypesForUnitTest() { 096 mySupportedSubscriptionTypes.clear(); 097 } 098 099 /** 100 * If e-mail subscriptions are supported, the From address used when sending e-mails 101 */ 102 public String getEmailFromAddress() { 103 return myEmailFromAddress; 104 } 105 106 /** 107 * Set the from address used for sending emails when using email subscriptions 108 */ 109 public void setEmailFromAddress(String theEmailFromAddress) { 110 myEmailFromAddress = theEmailFromAddress; 111 } 112 113 /** 114 * If websocket subscriptions are enabled, this specifies the context path that listens to them. Default value "/websocket". 115 */ 116 public String getWebsocketContextPath() { 117 return myWebsocketContextPath; 118 } 119 120 /** 121 * Set the websocket endpoint context path to use when websocket subscriptions are enabled. Default value "/websocket". 122 */ 123 public void setWebsocketContextPath(String theWebsocketContextPath) { 124 myWebsocketContextPath = theWebsocketContextPath; 125 } 126 127 /** 128 * This setting returns whether the channel name should be qualified or not. 129 * 130 * @return whether the channel name is qualified or not 131 * @since 6.4.0 132 */ 133 public boolean isQualifySubscriptionMatchingChannelName() { 134 return myQualifySubscriptionMatchingChannelName; 135 } 136 137 /** 138 * This setting controls whether the channel name 139 * should be qualified or not. 140 * Default is true, ie, the channel name will be qualified. 141 * 142 * @since 6.4.0 143 */ 144 public void setQualifySubscriptionMatchingChannelName(boolean theQualifySubscriptionMatchingChannelName) { 145 myQualifySubscriptionMatchingChannelName = theQualifySubscriptionMatchingChannelName; 146 } 147 148 /** 149 * If enabled, the server will support cross-partition subscription. 150 * This subscription will be the responsible for all the requests from all the partitions on this server. 151 * For example, if the server has 3 partitions, P1, P2, P3 152 * The subscription will live in the DEFAULT partition. Resource posted to DEFAULT, P1, P2, and P3 will trigger this subscription. 153 * <p> 154 * Default is <code>false</code> 155 * </p> 156 * 157 * @since 5.7.0 158 */ 159 public boolean isCrossPartitionSubscriptionEnabled() { 160 return myCrossPartitionSubscriptionEnabled; 161 } 162 163 /** 164 * If enabled, the server will support cross-partition subscription. 165 * This subscription will be the responsible for all the requests from all the partitions on this server. 166 * For example, if the server has 3 partitions, P1, P2, P3 167 * The subscription will live in the DEFAULT partition. Resource posted to DEFAULT, P1, P2, and P3 will trigger this subscription. 168 * <p> 169 * Default is <code>false</code> 170 * </p> 171 * 172 * @since 5.7.0 173 */ 174 public void setCrossPartitionSubscriptionEnabled(boolean theAllowCrossPartitionSubscription) { 175 myCrossPartitionSubscriptionEnabled = theAllowCrossPartitionSubscription; 176 } 177 /** 178 * If set to true, the server will prevent the creation of Subscriptions which cannot be evaluated IN-MEMORY. This can improve 179 * overall server performance. 180 * 181 * @since 6.8.0 182 */ 183 public void setOnlyAllowInMemorySubscriptions(boolean theAllowOnlyInMemorySearchParams) { 184 myAllowOnlyInMemorySubscriptions = theAllowOnlyInMemorySearchParams; 185 } 186 187 /** 188 * If set to true, the server will prevent the creation of Subscriptions which cannot be evaluated IN-MEMORY. This can improve 189 * overall server performance. 190 * 191 * @since 6.8.0 192 * @return Returns the value of {@link #setOnlyAllowInMemorySubscriptions(boolean)} 193 */ 194 public boolean isOnlyAllowInMemorySubscriptions() { 195 return myAllowOnlyInMemorySubscriptions; 196 } 197 198 /** 199 * If set to <code>false</code> (default is true) the server will not use 200 * in-memory subscription searching and instead use the database matcher for all subscription 201 * criteria matching. 202 * <p> 203 * When there are subscriptions registered 204 * on the server, the default behaviour is to compare the changed resource to the 205 * subscription criteria directly in-memory without going out to the database. 206 * Certain types of subscription criteria, e.g. chained references of queries with 207 * qualifiers or prefixes, are not supported by the in-memory matcher and will fall back 208 * to a database matcher. 209 * <p> 210 * The database matcher performs a query against the 211 * database by prepending ?id=XYZ to the subscription criteria where XYZ is the id of the changed entity 212 * 213 * @since 3.6.1 214 */ 215 public boolean isEnableInMemorySubscriptionMatching() { 216 return myEnableInMemorySubscriptionMatching; 217 } 218 219 /** 220 * If set to <code>false</code> (default is true) the server will not use 221 * in-memory subscription searching and instead use the database matcher for all subscription 222 * criteria matching. 223 * <p> 224 * When there are subscriptions registered 225 * on the server, the default behaviour is to compare the changed resource to the 226 * subscription criteria directly in-memory without going out to the database. 227 * Certain types of subscription criteria, e.g. chained references of queries with 228 * qualifiers or prefixes, are not supported by the in-memory matcher and will fall back 229 * to a database matcher. 230 * <p> 231 * The database matcher performs a query against the 232 * database by prepending ?id=XYZ to the subscription criteria where XYZ is the id of the changed entity 233 * 234 * @since 3.6.1 235 */ 236 public void setEnableInMemorySubscriptionMatching(boolean theEnableInMemorySubscriptionMatching) { 237 myEnableInMemorySubscriptionMatching = theEnableInMemorySubscriptionMatching; 238 } 239 240 /** 241 * If set to true (default is false) then subscriptions will be triggered for resource updates even if they 242 * do not trigger a new version (e.g. $meta-add and $meta-delete). 243 * 244 * @since 5.5.0 245 */ 246 public boolean isTriggerSubscriptionsForNonVersioningChanges() { 247 return myTriggerSubscriptionsForNonVersioningChanges; 248 } 249 250 /** 251 * If set to true (default is false) then subscriptions will be triggered for resource updates even if they 252 * do not trigger a new version (e.g. $meta-add and $meta-delete). 253 * 254 * @since 5.5.0 255 */ 256 public void setTriggerSubscriptionsForNonVersioningChanges(boolean theTriggerSubscriptionsForNonVersioningChanges) { 257 myTriggerSubscriptionsForNonVersioningChanges = theTriggerSubscriptionsForNonVersioningChanges; 258 } 259 260 /** 261 * Provides the regex expression to perform endpoint URL validation If rest-hook subscriptions are supported. 262 * Default value is {@link #DEFAULT_RESTHOOK_ENDPOINTURL_VALIDATION_REGEX}. 263 * @since 7.6.0 264 */ 265 public String getRestHookEndpointUrlValidationRegex() { 266 return myRestHookEndpointUrlValidationRegex; 267 } 268 269 /** 270 * Configure the regex expression that will be used to validate the endpoint URL. 271 * Set to NULL or EMPTY for no endpoint URL validation. 272 * 273 * @since 7.6.0 274 */ 275 public void setRestHookEndpointUrlValidationRegex(String theRestHookEndpointUrlValidationgRegex) { 276 myRestHookEndpointUrlValidationRegex = theRestHookEndpointUrlValidationgRegex; 277 } 278 279 /** 280 * Whether an endpoint validation Regex was set for URL validation. 281 * 282 * @since 7.6.0 283 */ 284 public boolean hasRestHookEndpointUrlValidationRegex() { 285 return isNotBlank(myRestHookEndpointUrlValidationRegex); 286 } 287 288 /** 289 * If this is enabled (default is {@literal false}), changes to Subscription resource would be put on queue immediately. 290 * Reducing delay between creation of the Subscription and Activation. 291 * 292 * @since 7.8.0 293 */ 294 public boolean isSubscriptionChangeQueuedImmediately() { 295 return mySubscriptionChangeQueuedImmediately; 296 } 297 298 /** 299 * If this is enabled (default is {@literal false}), changes to Subscription resource would be put on queue immediately. 300 * Reducing delay between creation of the Subscription and Activation. 301 * 302 * @since 7.8.0 303 */ 304 public void setSubscriptionChangeQueuedImmediately(boolean theSubscriptionChangeQueuedImmediately) { 305 mySubscriptionChangeQueuedImmediately = theSubscriptionChangeQueuedImmediately; 306 } 307 308 /** 309 * The interval in which the Resource Changes will be polled from DB. Defaults to {@literal 5000}. 310 * 311 * @since 7.7.0 312 */ 313 public long getSubscriptionIntervalInMs() { 314 return mySubmissionIntervalInMs; 315 } 316 317 /** 318 * The interval in which the Resource Changes will be polled from DB. Defaults to {@literal 5000}. 319 * 320 * @since 7.7.0 321 */ 322 public void setSubscriptionIntervalInMs(long theSubscriptionIntervalInMs) { 323 mySubmissionIntervalInMs = theSubscriptionIntervalInMs; 324 } 325}