
001package ca.uhn.fhir.jaxrs.server.util; 002 003/* 004 * #%L 005 * HAPI FHIR JAX-RS Server 006 * %% 007 * Copyright (C) 2014 - 2022 Smile CDR, Inc. 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.context.ConfigurationException; 025import ca.uhn.fhir.context.FhirContext; 026import ca.uhn.fhir.context.FhirVersionEnum; 027import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider; 028import ca.uhn.fhir.rest.api.Constants; 029import ca.uhn.fhir.rest.api.RequestTypeEnum; 030import ca.uhn.fhir.rest.api.RestOperationTypeEnum; 031import ca.uhn.fhir.rest.api.server.IRestfulResponse; 032import ca.uhn.fhir.rest.api.server.RequestDetails; 033import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 034import ca.uhn.fhir.rest.server.method.ResourceParameter; 035import ca.uhn.fhir.util.UrlUtil; 036import org.apache.commons.lang3.StringUtils; 037 038import javax.ws.rs.core.HttpHeaders; 039import javax.ws.rs.core.MediaType; 040import java.io.IOException; 041import java.io.InputStream; 042import java.io.Reader; 043import java.nio.charset.Charset; 044import java.util.Collections; 045import java.util.HashMap; 046import java.util.List; 047import java.util.Map; 048 049/** 050 * The JaxRsRequest is a jax-rs specific implementation of the RequestDetails. 051 * 052 * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare 053 */ 054public class JaxRsRequest extends RequestDetails { 055 056 private HttpHeaders myHeaders; 057 private String myResourceString; 058 private AbstractJaxRsProvider myServer; 059 private Map<String, Object> myAttributes = new HashMap<>(); 060 061 /** 062 * Utility Constructor 063 * 064 * @param server the server 065 * @param resourceString the resource body 066 * @param requestType the request type 067 * @param restOperation the operation type 068 */ 069 public JaxRsRequest(AbstractJaxRsProvider server, String resourceString, RequestTypeEnum requestType, 070 RestOperationTypeEnum restOperation) { 071 super(server.getInterceptorService()); 072 this.myHeaders = server.getHeaders(); 073 this.myResourceString = resourceString; 074 this.setRestOperationType(restOperation); 075 setServer(server); 076 setFhirServerBase(server.getBaseForServer()); 077 setParameters(server.getParameters()); 078 setRequestType(requestType); 079 } 080 081 @Override 082 protected byte[] getByteStreamRequestContents() { 083 return StringUtils.defaultString(myResourceString, "") 084 .getBytes(ResourceParameter.determineRequestCharset(this)); 085 } 086 087 @Override 088 public Charset getCharset() { 089 String charset = null; 090 091 if (myHeaders.getMediaType() != null && myHeaders.getMediaType().getParameters() != null) { 092 charset = myHeaders.getMediaType().getParameters().get(MediaType.CHARSET_PARAMETER); 093 } 094 if (charset != null) { 095 return Charset.forName(charset); 096 } else { 097 return null; 098 } 099 } 100 101 @Override 102 public FhirContext getFhirContext() { 103 return myServer.getFhirContext(); 104 } 105 106 @Override 107 public String getHeader(String headerKey) { 108 List<String> requestHeader = getHeaders(headerKey); 109 return requestHeader.isEmpty() ? null : requestHeader.get(0); 110 } 111 112 @Override 113 public List<String> getHeaders(String name) { 114 List<String> requestHeader = myHeaders.getRequestHeader(name); 115 return requestHeader == null ? Collections.<String>emptyList() : requestHeader; 116 } 117 118 @Override 119 public Object getAttribute(String theAttributeName) { 120 return myAttributes.get(theAttributeName); 121 } 122 123 @Override 124 public void setAttribute(String theAttributeName, Object theAttributeValue) { 125 myAttributes.put(theAttributeName, theAttributeValue); 126 } 127 128 @Override 129 public InputStream getInputStream() { 130 // not yet implemented 131 throw new UnsupportedOperationException(Msg.code(599)); 132 } 133 134 @Override 135 public Reader getReader() throws IOException { 136 // not yet implemented 137 throw new UnsupportedOperationException(Msg.code(600)); 138 } 139 140 @Override 141 public IRestfulResponse getResponse() { 142 if (super.getResponse() == null) { 143 setResponse(new JaxRsResponse(this)); 144 } 145 return super.getResponse(); 146 } 147 148 @Override 149 public AbstractJaxRsProvider getServer() { 150 return myServer; 151 } 152 153 /** 154 * Set the server 155 * 156 * @param theServer the server to set 157 */ 158 public void setServer(AbstractJaxRsProvider theServer) { 159 this.myServer = theServer; 160 } 161 162 @Override 163 public String getServerBaseForRequest() { 164 return getServer().getServerAddressStrategy().determineServerBase(null, null); 165 } 166 167 /** 168 * An implementation of the builder pattern for the JaxRsRequest 169 */ 170 public static class Builder { 171 private final String myResourceName; 172 private String myCompartment; 173 private String myId; 174 private RequestTypeEnum myRequestType; 175 private String myRequestUrl; 176 private String myResource; 177 private RestOperationTypeEnum myRestOperation; 178 private AbstractJaxRsProvider myServer; 179 private String myVersion; 180 181 /** 182 * Utility Constructor 183 * 184 * @param theServer the server 185 * @param theRequestType the request type 186 * @param theRestOperation the rest operation 187 * @param theRequestUrl 188 */ 189 public Builder(AbstractJaxRsProvider theServer, RequestTypeEnum theRequestType, 190 RestOperationTypeEnum theRestOperation, String theRequestUrl, String theResourceName) { 191 this.myServer = theServer; 192 this.myRequestType = theRequestType; 193 this.myRestOperation = theRestOperation; 194 this.myRequestUrl = theRequestUrl; 195 this.myResourceName = theResourceName; 196 } 197 198 /** 199 * Create the jax-rs request 200 * 201 * @return the jax-rs request 202 */ 203 public JaxRsRequest build() { 204 JaxRsRequest result = new JaxRsRequest(myServer, myResource, myRequestType, myRestOperation); 205 if ((StringUtils.isNotBlank(myVersion) || StringUtils.isNotBlank(myCompartment)) 206 && StringUtils.isBlank(myId)) { 207 throw new InvalidRequestException(Msg.code(601) + "Don't know how to handle request path: " 208 + myServer.getUriInfo().getRequestUri().toASCIIString()); 209 } 210 211 FhirVersionEnum fhirContextVersion = myServer.getFhirContext().getVersion().getVersion(); 212 213 if (StringUtils.isNotBlank(myVersion)) { 214 switch (fhirContextVersion) { 215 case R4: 216 result.setId(new org.hl7.fhir.r4.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion))); 217 break; 218 case DSTU3: 219 result.setId(new org.hl7.fhir.dstu3.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion))); 220 break; 221 case DSTU2_1: 222 result.setId(new org.hl7.fhir.dstu2016may.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion))); 223 break; 224 case DSTU2_HL7ORG: 225 result.setId(new org.hl7.fhir.dstu2.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion))); 226 break; 227 case DSTU2: 228 result.setId(new ca.uhn.fhir.model.primitive.IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion))); 229 break; 230 default: 231 throw new ConfigurationException(Msg.code(602) + "Unsupported Fhir version: " + fhirContextVersion); 232 } 233 } else if (StringUtils.isNotBlank(myId)) { 234 switch (fhirContextVersion) { 235 case R4: 236 result.setId(new org.hl7.fhir.r4.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId))); 237 break; 238 case DSTU3: 239 result.setId(new org.hl7.fhir.dstu3.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId))); 240 break; 241 case DSTU2_1: 242 result.setId(new org.hl7.fhir.dstu2016may.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId))); 243 break; 244 case DSTU2_HL7ORG: 245 result.setId(new org.hl7.fhir.dstu2.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId))); 246 break; 247 case DSTU2: 248 result.setId(new ca.uhn.fhir.model.primitive.IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId))); 249 break; 250 default: 251 throw new ConfigurationException(Msg.code(603) + "Unsupported Fhir version: " + fhirContextVersion); 252 } 253 } 254 255 if (myRestOperation == RestOperationTypeEnum.UPDATE) { 256 String contentLocation = result.getHeader(Constants.HEADER_CONTENT_LOCATION); 257 if (contentLocation != null) { 258 switch (fhirContextVersion) { 259 case R4: 260 result.setId(new org.hl7.fhir.r4.model.IdType(contentLocation)); 261 break; 262 case DSTU3: 263 result.setId(new org.hl7.fhir.dstu3.model.IdType(contentLocation)); 264 break; 265 case DSTU2_1: 266 result.setId(new org.hl7.fhir.dstu2016may.model.IdType(contentLocation)); 267 break; 268 case DSTU2_HL7ORG: 269 result.setId(new org.hl7.fhir.dstu2.model.IdType(contentLocation)); 270 break; 271 case DSTU2: 272 result.setId(new ca.uhn.fhir.model.primitive.IdDt(contentLocation)); 273 break; 274 default: 275 throw new ConfigurationException(Msg.code(604) + "Unsupported Fhir version: " + fhirContextVersion); 276 } 277 } 278 } 279 280 result.setCompartmentName(myCompartment); 281 result.setCompleteUrl(myRequestUrl); 282 result.setResourceName(myResourceName); 283 284 return result; 285 } 286 287 /** 288 * Set the compartment 289 * 290 * @param compartment the compartment 291 * @return the builder 292 */ 293 public Builder compartment(String compartment) { 294 this.myCompartment = compartment; 295 return this; 296 } 297 298 /** 299 * Set the id 300 * 301 * @param id the resource id 302 * @return the builder 303 */ 304 public Builder id(String id) { 305 this.myId = id; 306 return this; 307 } 308 309 /** 310 * Set the resource 311 * 312 * @param resource the body contents of an http method 313 * @return the builder 314 */ 315 public Builder resource(String resource) { 316 this.myResource = resource; 317 return this; 318 } 319 320 /** 321 * Set the id version 322 * 323 * @param version the version of the resource 324 * @return the builder 325 */ 326 public Builder version(String version) { 327 this.myVersion = version; 328 return this; 329 } 330 } 331}