
001/*- 002 * #%L 003 * HAPI FHIR - Server Framework 004 * %% 005 * Copyright (C) 2014 - 2025 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.rest.api.server; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.context.api.AddProfileTagEnum; 024import ca.uhn.fhir.interceptor.api.HookParams; 025import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; 026import ca.uhn.fhir.interceptor.api.IInterceptorService; 027import ca.uhn.fhir.interceptor.api.Pointcut; 028import ca.uhn.fhir.interceptor.model.RequestPartitionId; 029import ca.uhn.fhir.rest.api.EncodingEnum; 030import ca.uhn.fhir.rest.server.ETagSupportEnum; 031import ca.uhn.fhir.rest.server.ElementsSupportEnum; 032import ca.uhn.fhir.rest.server.IPagingProvider; 033import ca.uhn.fhir.rest.server.IRestfulServerDefaults; 034import ca.uhn.fhir.rest.server.RestfulServer; 035import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; 036import com.google.common.collect.ImmutableListMultimap; 037import com.google.common.collect.ListMultimap; 038import com.google.common.collect.MultimapBuilder; 039 040import java.io.IOException; 041import java.io.InputStream; 042import java.io.Reader; 043import java.nio.charset.Charset; 044import java.util.Collections; 045import java.util.List; 046 047import static java.util.Objects.nonNull; 048 049/** 050 * A default RequestDetails implementation that can be used for system calls to 051 * Resource DAO methods when partitioning is enabled. Using a SystemRequestDetails 052 * instance for system calls will ensure that any resource queries or updates will 053 * use the DEFAULT partition when partitioning is enabled. 054 */ 055public class SystemRequestDetails extends RequestDetails { 056 private FhirContext myFhirContext; 057 private ListMultimap<String, String> myHeaders; 058 /** 059 * If a SystemRequestDetails has a RequestPartitionId, it will take precedence over the tenantId 060 */ 061 private RequestPartitionId myRequestPartitionId; 062 063 private IRestfulServerDefaults myServer = new MyRestfulServerDefaults(); 064 065 public SystemRequestDetails() { 066 this(new MyInterceptorBroadcaster()); 067 } 068 069 public SystemRequestDetails(IInterceptorBroadcaster theInterceptorBroadcaster) { 070 super(theInterceptorBroadcaster); 071 } 072 073 public SystemRequestDetails(RequestDetails theDetails) { 074 super(theDetails); 075 if (nonNull(theDetails.getServer())) { 076 myServer = theDetails.getServer(); 077 myFhirContext = theDetails.getFhirContext(); 078 } 079 } 080 081 /** 082 * Copy constructor 083 * @param theOther The request details to copy from 084 */ 085 public SystemRequestDetails(SystemRequestDetails theOther) { 086 super(theOther); 087 if (nonNull(theOther.getServer())) { 088 myServer = theOther.getServer(); 089 myFhirContext = theOther.getFhirContext(); 090 } 091 if (nonNull(theOther.myHeaders)) { 092 initHeaderMap(); 093 myHeaders.putAll(theOther.myHeaders); 094 } 095 myRequestPartitionId = theOther.myRequestPartitionId; 096 } 097 098 // TODO KHS use this everywhere we create a srd with only one partition 099 public static SystemRequestDetails forRequestPartitionId(RequestPartitionId thePartitionId) { 100 SystemRequestDetails retVal = new SystemRequestDetails(); 101 retVal.setRequestPartitionId(thePartitionId); 102 return retVal; 103 } 104 105 public RequestPartitionId getRequestPartitionId() { 106 return myRequestPartitionId; 107 } 108 109 public SystemRequestDetails setRequestPartitionId(RequestPartitionId theRequestPartitionId) { 110 myRequestPartitionId = theRequestPartitionId; 111 return this; 112 } 113 114 @Override 115 protected byte[] getByteStreamRequestContents() { 116 return new byte[0]; 117 } 118 119 @Override 120 public Charset getCharset() { 121 return null; 122 } 123 124 @Override 125 public FhirContext getFhirContext() { 126 return myFhirContext; 127 } 128 129 public void setFhirContext(FhirContext theFhirContext) { 130 myFhirContext = theFhirContext; 131 } 132 133 @Override 134 public String getHeader(String name) { 135 List<String> headers = getHeaders(name); 136 if (headers.isEmpty()) { 137 return null; 138 } else { 139 return headers.get(0); 140 } 141 } 142 143 @Override 144 public List<String> getHeaders(String name) { 145 ListMultimap<String, String> headers = myHeaders; 146 if (headers == null) { 147 headers = ImmutableListMultimap.of(); 148 } 149 return headers.get(name); 150 } 151 152 @Override 153 public void addHeader(String theName, String theValue) { 154 initHeaderMap(); 155 myHeaders.put(theName, theValue); 156 } 157 158 @Override 159 public void setHeaders(String theName, List<String> theValues) { 160 initHeaderMap(); 161 myHeaders.putAll(theName, theValues); 162 } 163 164 private void initHeaderMap() { 165 if (myHeaders == null) { 166 // Make sure we are case-insensitive on keys 167 myHeaders = MultimapBuilder.treeKeys(String.CASE_INSENSITIVE_ORDER) 168 .arrayListValues() 169 .build(); 170 } 171 } 172 173 @Override 174 public Object getAttribute(String theAttributeName) { 175 return null; 176 } 177 178 @Override 179 public void setAttribute(String theAttributeName, Object theAttributeValue) {} 180 181 @Override 182 public InputStream getInputStream() throws IOException { 183 return null; 184 } 185 186 @Override 187 public Reader getReader() { 188 return null; 189 } 190 191 @Override 192 public IRestfulServerDefaults getServer() { 193 return myServer; 194 } 195 196 public void setServer(RestfulServer theServer) { 197 this.myServer = theServer; 198 } 199 200 @Override 201 public String getServerBaseForRequest() { 202 return null; 203 } 204 205 private static class MyRestfulServerDefaults implements IRestfulServerDefaults { 206 207 @Override 208 public AddProfileTagEnum getAddProfileTag() { 209 return null; 210 } 211 212 @Override 213 public EncodingEnum getDefaultResponseEncoding() { 214 return null; 215 } 216 217 @Override 218 public ETagSupportEnum getETagSupport() { 219 return null; 220 } 221 222 @Override 223 public ElementsSupportEnum getElementsSupport() { 224 return null; 225 } 226 227 @Override 228 public FhirContext getFhirContext() { 229 return null; 230 } 231 232 @Override 233 public List<IServerInterceptor> getInterceptors_() { 234 return null; 235 } 236 237 @Override 238 public IPagingProvider getPagingProvider() { 239 return null; 240 } 241 242 @Override 243 public boolean isDefaultPrettyPrint() { 244 return false; 245 } 246 247 @Override 248 public IInterceptorService getInterceptorService() { 249 return null; 250 } 251 } 252 253 private static class MyInterceptorBroadcaster implements IInterceptorBroadcaster { 254 255 @Override 256 public boolean callHooks(Pointcut thePointcut, HookParams theParams) { 257 return true; 258 } 259 260 @Override 261 public Object callHooksAndReturnObject(Pointcut thePointcut, HookParams theParams) { 262 return null; 263 } 264 265 @Override 266 public boolean hasHooks(Pointcut thePointcut) { 267 return false; 268 } 269 270 @Override 271 public List<IInvoker> getInvokersForPointcut(Pointcut thePointcut) { 272 return Collections.emptyList(); 273 } 274 } 275 276 public static SystemRequestDetails forAllPartitions() { 277 return new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.allPartitions()); 278 } 279 280 public static SystemRequestDetails newSystemRequestAllPartitions() { 281 SystemRequestDetails systemRequestDetails = new SystemRequestDetails(); 282 systemRequestDetails.setRequestPartitionId(RequestPartitionId.allPartitions()); 283 return systemRequestDetails; 284 } 285}