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 038 private final Set<Subscription.SubscriptionChannelType> mySupportedSubscriptionTypes = new HashSet<>(); 039 private String myEmailFromAddress = DEFAULT_EMAIL_FROM_ADDRESS; 040 private String myWebsocketContextPath = DEFAULT_WEBSOCKET_CONTEXT_PATH; 041 private boolean myQualifySubscriptionMatchingChannelName = true; 042 private boolean myCrossPartitionSubscriptionEnabled = true; 043 private boolean myEnableInMemorySubscriptionMatching = true; 044 private boolean myTriggerSubscriptionsForNonVersioningChanges; 045 046 /** 047 * @since 6.8.0 048 * Prevents any non IN-MEMORY Search params from being created by users. 049 */ 050 private boolean myAllowOnlyInMemorySubscriptions = false; 051 052 /** 053 * @since 7.6.0 054 * 055 * Regex To perform validation on the endpoint URL for Subscription of type RESTHOOK. 056 */ 057 private String myRestHookEndpointUrlValidationRegex = DEFAULT_RESTHOOK_ENDPOINTURL_VALIDATION_REGEX; 058 059 /** 060 * This setting indicates which subscription channel types are supported by the server. Any subscriptions submitted 061 * to the server matching these types will be activated. 062 */ 063 public void addSupportedSubscriptionType(Subscription.SubscriptionChannelType theSubscriptionChannelType) { 064 mySupportedSubscriptionTypes.add(theSubscriptionChannelType); 065 } 066 067 /** 068 * This setting indicates which subscription channel types are supported by the server. Any subscriptions submitted 069 * to the server matching these types will be activated. 070 */ 071 public Set<Subscription.SubscriptionChannelType> getSupportedSubscriptionTypes() { 072 return Collections.unmodifiableSet(mySupportedSubscriptionTypes); 073 } 074 075 /** 076 * Indicate whether a subscription channel type is supported by this server. 077 * 078 * @return true if at least one subscription channel type is supported by this server false otherwise. 079 */ 080 public boolean hasSupportedSubscriptionTypes() { 081 return CollectionUtils.isNotEmpty(mySupportedSubscriptionTypes); 082 } 083 084 @VisibleForTesting 085 public void clearSupportedSubscriptionTypesForUnitTest() { 086 mySupportedSubscriptionTypes.clear(); 087 } 088 089 /** 090 * If e-mail subscriptions are supported, the From address used when sending e-mails 091 */ 092 public String getEmailFromAddress() { 093 return myEmailFromAddress; 094 } 095 096 /** 097 * Set the from address used for sending emails when using email subscriptions 098 */ 099 public void setEmailFromAddress(String theEmailFromAddress) { 100 myEmailFromAddress = theEmailFromAddress; 101 } 102 103 /** 104 * If websocket subscriptions are enabled, this specifies the context path that listens to them. Default value "/websocket". 105 */ 106 public String getWebsocketContextPath() { 107 return myWebsocketContextPath; 108 } 109 110 /** 111 * Set the websocket endpoint context path to use when websocket subscriptions are enabled. Default value "/websocket". 112 */ 113 public void setWebsocketContextPath(String theWebsocketContextPath) { 114 myWebsocketContextPath = theWebsocketContextPath; 115 } 116 117 /** 118 * This setting returns whether the channel name should be qualified or not. 119 * 120 * @return whether the channel name is qualified or not 121 * @since 6.4.0 122 */ 123 public boolean isQualifySubscriptionMatchingChannelName() { 124 return myQualifySubscriptionMatchingChannelName; 125 } 126 127 /** 128 * This setting controls whether the channel name 129 * should be qualified or not. 130 * Default is true, ie, the channel name will be qualified. 131 * 132 * @since 6.4.0 133 */ 134 public void setQualifySubscriptionMatchingChannelName(boolean theQualifySubscriptionMatchingChannelName) { 135 myQualifySubscriptionMatchingChannelName = theQualifySubscriptionMatchingChannelName; 136 } 137 138 /** 139 * If enabled, the server will support cross-partition subscription. 140 * This subscription will be the responsible for all the requests from all the partitions on this server. 141 * For example, if the server has 3 partitions, P1, P2, P3 142 * The subscription will live in the DEFAULT partition. Resource posted to DEFAULT, P1, P2, and P3 will trigger this subscription. 143 * <p> 144 * Default is <code>false</code> 145 * </p> 146 * 147 * @since 5.7.0 148 */ 149 public boolean isCrossPartitionSubscriptionEnabled() { 150 return myCrossPartitionSubscriptionEnabled; 151 } 152 153 /** 154 * If enabled, the server will support cross-partition subscription. 155 * This subscription will be the responsible for all the requests from all the partitions on this server. 156 * For example, if the server has 3 partitions, P1, P2, P3 157 * The subscription will live in the DEFAULT partition. Resource posted to DEFAULT, P1, P2, and P3 will trigger this subscription. 158 * <p> 159 * Default is <code>false</code> 160 * </p> 161 * 162 * @since 5.7.0 163 */ 164 public void setCrossPartitionSubscriptionEnabled(boolean theAllowCrossPartitionSubscription) { 165 myCrossPartitionSubscriptionEnabled = theAllowCrossPartitionSubscription; 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 */ 173 public void setOnlyAllowInMemorySubscriptions(boolean theAllowOnlyInMemorySearchParams) { 174 myAllowOnlyInMemorySubscriptions = theAllowOnlyInMemorySearchParams; 175 } 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 * @return Returns the value of {@link #setOnlyAllowInMemorySubscriptions(boolean)} 183 */ 184 public boolean isOnlyAllowInMemorySubscriptions() { 185 return myAllowOnlyInMemorySubscriptions; 186 } 187 188 /** 189 * If set to <code>false</code> (default is true) the server will not use 190 * in-memory subscription searching and instead use the database matcher for all subscription 191 * criteria matching. 192 * <p> 193 * When there are subscriptions registered 194 * on the server, the default behaviour is to compare the changed resource to the 195 * subscription criteria directly in-memory without going out to the database. 196 * Certain types of subscription criteria, e.g. chained references of queries with 197 * qualifiers or prefixes, are not supported by the in-memory matcher and will fall back 198 * to a database matcher. 199 * <p> 200 * The database matcher performs a query against the 201 * database by prepending ?id=XYZ to the subscription criteria where XYZ is the id of the changed entity 202 * 203 * @since 3.6.1 204 */ 205 public boolean isEnableInMemorySubscriptionMatching() { 206 return myEnableInMemorySubscriptionMatching; 207 } 208 209 /** 210 * If set to <code>false</code> (default is true) the server will not use 211 * in-memory subscription searching and instead use the database matcher for all subscription 212 * criteria matching. 213 * <p> 214 * When there are subscriptions registered 215 * on the server, the default behaviour is to compare the changed resource to the 216 * subscription criteria directly in-memory without going out to the database. 217 * Certain types of subscription criteria, e.g. chained references of queries with 218 * qualifiers or prefixes, are not supported by the in-memory matcher and will fall back 219 * to a database matcher. 220 * <p> 221 * The database matcher performs a query against the 222 * database by prepending ?id=XYZ to the subscription criteria where XYZ is the id of the changed entity 223 * 224 * @since 3.6.1 225 */ 226 public void setEnableInMemorySubscriptionMatching(boolean theEnableInMemorySubscriptionMatching) { 227 myEnableInMemorySubscriptionMatching = theEnableInMemorySubscriptionMatching; 228 } 229 230 /** 231 * If set to true (default is false) then subscriptions will be triggered for resource updates even if they 232 * do not trigger a new version (e.g. $meta-add and $meta-delete). 233 * 234 * @since 5.5.0 235 */ 236 public boolean isTriggerSubscriptionsForNonVersioningChanges() { 237 return myTriggerSubscriptionsForNonVersioningChanges; 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 void setTriggerSubscriptionsForNonVersioningChanges(boolean theTriggerSubscriptionsForNonVersioningChanges) { 247 myTriggerSubscriptionsForNonVersioningChanges = theTriggerSubscriptionsForNonVersioningChanges; 248 } 249 250 /** 251 * Provides the regex expression to perform endpoint URL validation If rest-hook subscriptions are supported. 252 * Default value is {@link #DEFAULT_RESTHOOK_ENDPOINTURL_VALIDATION_REGEX}. 253 * @since 7.6.0 254 */ 255 public String getRestHookEndpointUrlValidationRegex() { 256 return myRestHookEndpointUrlValidationRegex; 257 } 258 259 /** 260 * Configure the regex expression that will be used to validate the endpoint URL. 261 * Set to NULL or EMPTY for no endpoint URL validation. 262 * 263 * @since 7.6.0 264 */ 265 public void setRestHookEndpointUrlValidationRegex(String theRestHookEndpointUrlValidationgRegex) { 266 myRestHookEndpointUrlValidationRegex = theRestHookEndpointUrlValidationgRegex; 267 } 268 269 /** 270 * Whether an endpoint validation Regex was set for URL validation. 271 * 272 * @since 7.6.0 273 */ 274 public boolean hasRestHookEndpointUrlValidationRegex() { 275 return isNotBlank(myRestHookEndpointUrlValidationRegex); 276 } 277}