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}