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 // TODO KHS use this everywhere we create a srd with only one partition 082 public static SystemRequestDetails forRequestPartitionId(RequestPartitionId thePartitionId) { 083 SystemRequestDetails retVal = new SystemRequestDetails(); 084 retVal.setRequestPartitionId(thePartitionId); 085 return retVal; 086 } 087 088 public RequestPartitionId getRequestPartitionId() { 089 return myRequestPartitionId; 090 } 091 092 public SystemRequestDetails setRequestPartitionId(RequestPartitionId theRequestPartitionId) { 093 myRequestPartitionId = theRequestPartitionId; 094 return this; 095 } 096 097 @Override 098 protected byte[] getByteStreamRequestContents() { 099 return new byte[0]; 100 } 101 102 @Override 103 public Charset getCharset() { 104 return null; 105 } 106 107 @Override 108 public FhirContext getFhirContext() { 109 return myFhirContext; 110 } 111 112 public void setFhirContext(FhirContext theFhirContext) { 113 myFhirContext = theFhirContext; 114 } 115 116 @Override 117 public String getHeader(String name) { 118 List<String> headers = getHeaders(name); 119 if (headers.isEmpty()) { 120 return null; 121 } else { 122 return headers.get(0); 123 } 124 } 125 126 @Override 127 public List<String> getHeaders(String name) { 128 ListMultimap<String, String> headers = myHeaders; 129 if (headers == null) { 130 headers = ImmutableListMultimap.of(); 131 } 132 return headers.get(name); 133 } 134 135 @Override 136 public void addHeader(String theName, String theValue) { 137 initHeaderMap(); 138 myHeaders.put(theName, theValue); 139 } 140 141 @Override 142 public void setHeaders(String theName, List<String> theValues) { 143 initHeaderMap(); 144 myHeaders.putAll(theName, theValues); 145 } 146 147 private void initHeaderMap() { 148 if (myHeaders == null) { 149 // Make sure we are case-insensitive on keys 150 myHeaders = MultimapBuilder.treeKeys(String.CASE_INSENSITIVE_ORDER) 151 .arrayListValues() 152 .build(); 153 } 154 } 155 156 @Override 157 public Object getAttribute(String theAttributeName) { 158 return null; 159 } 160 161 @Override 162 public void setAttribute(String theAttributeName, Object theAttributeValue) {} 163 164 @Override 165 public InputStream getInputStream() throws IOException { 166 return null; 167 } 168 169 @Override 170 public Reader getReader() { 171 return null; 172 } 173 174 @Override 175 public IRestfulServerDefaults getServer() { 176 return myServer; 177 } 178 179 public void setServer(RestfulServer theServer) { 180 this.myServer = theServer; 181 } 182 183 @Override 184 public String getServerBaseForRequest() { 185 return null; 186 } 187 188 private static class MyRestfulServerDefaults implements IRestfulServerDefaults { 189 190 @Override 191 public AddProfileTagEnum getAddProfileTag() { 192 return null; 193 } 194 195 @Override 196 public EncodingEnum getDefaultResponseEncoding() { 197 return null; 198 } 199 200 @Override 201 public ETagSupportEnum getETagSupport() { 202 return null; 203 } 204 205 @Override 206 public ElementsSupportEnum getElementsSupport() { 207 return null; 208 } 209 210 @Override 211 public FhirContext getFhirContext() { 212 return null; 213 } 214 215 @Override 216 public List<IServerInterceptor> getInterceptors_() { 217 return null; 218 } 219 220 @Override 221 public IPagingProvider getPagingProvider() { 222 return null; 223 } 224 225 @Override 226 public boolean isDefaultPrettyPrint() { 227 return false; 228 } 229 230 @Override 231 public IInterceptorService getInterceptorService() { 232 return null; 233 } 234 } 235 236 private static class MyInterceptorBroadcaster implements IInterceptorBroadcaster { 237 238 @Override 239 public boolean callHooks(Pointcut thePointcut, HookParams theParams) { 240 return true; 241 } 242 243 @Override 244 public Object callHooksAndReturnObject(Pointcut thePointcut, HookParams theParams) { 245 return null; 246 } 247 248 @Override 249 public boolean hasHooks(Pointcut thePointcut) { 250 return false; 251 } 252 253 @Override 254 public List<IInvoker> getInvokersForPointcut(Pointcut thePointcut) { 255 return Collections.emptyList(); 256 } 257 } 258 259 public static SystemRequestDetails forAllPartitions() { 260 return new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.allPartitions()); 261 } 262 263 public static SystemRequestDetails newSystemRequestAllPartitions() { 264 SystemRequestDetails systemRequestDetails = new SystemRequestDetails(); 265 systemRequestDetails.setRequestPartitionId(RequestPartitionId.allPartitions()); 266 return systemRequestDetails; 267 } 268}