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