001package ca.uhn.fhir.jaxrs.server.util;
002
003/*
004 * #%L
005 * HAPI FHIR JAX-RS Server
006 * %%
007 * Copyright (C) 2014 - 2016 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 java.io.IOException;
024import java.io.InputStream;
025import java.io.Reader;
026import java.nio.charset.Charset;
027import java.util.Collections;
028import java.util.List;
029
030import javax.ws.rs.core.HttpHeaders;
031import javax.ws.rs.core.MediaType;
032
033import org.apache.commons.lang3.StringUtils;
034import org.hl7.fhir.dstu3.model.IdType;
035
036import ca.uhn.fhir.context.FhirVersionEnum;
037import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
038import ca.uhn.fhir.model.primitive.IdDt;
039import ca.uhn.fhir.rest.api.RequestTypeEnum;
040import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
041import ca.uhn.fhir.rest.method.RequestDetails;
042import ca.uhn.fhir.rest.param.ResourceParameter;
043import ca.uhn.fhir.rest.server.Constants;
044import ca.uhn.fhir.rest.server.IRestfulResponse;
045import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
046import ca.uhn.fhir.util.UrlUtil;
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        /**
056         * An implementation of the builder pattern for the JaxRsRequest
057         */
058        public static class Builder {
059                private String myResource;
060                private AbstractJaxRsProvider myServer;
061                private RequestTypeEnum myRequestType;
062                private RestOperationTypeEnum myRestOperation;
063                private String myId;
064                private String myVersion;
065                private String myCompartment;
066                private String myRequestUrl;
067        private final String myResourceName;            
068
069                /**
070                 * Utility Constructor
071                 * @param theServer the server
072                 * @param theRequestType the request type
073                 * @param theRestOperation the rest operation
074                 * @param theRequestUrl 
075                 */
076                public Builder(AbstractJaxRsProvider theServer, RequestTypeEnum theRequestType,
077                                RestOperationTypeEnum theRestOperation, String theRequestUrl, String theResourceName) {
078                        this.myServer = theServer;
079                        this.myRequestType = theRequestType;
080                        this.myRestOperation = theRestOperation;
081                        this.myRequestUrl = theRequestUrl;
082                        this.myResourceName = theResourceName;
083                }
084
085                /**
086                 * Set the resource
087                 * @param resource the body contents of an http method 
088                 * @return the builder
089                 */
090                public Builder resource(String resource) {
091                        this.myResource = resource;
092                        return this;
093                }
094
095                /**
096                 * Set the id
097                 * @param id the resource id
098                 * @return the builder
099                 */
100                public Builder id(String id) {
101                        this.myId = id;
102                        return this;
103                }
104
105                /**
106                 * Set the id version
107                 * @param version the version of the resource
108                 * @return the builder
109                 */
110                public Builder version(String version) {
111                        this.myVersion = version;
112                        return this;
113                }
114
115                /**
116                 * Set the compartment
117                 * @param compartment the compartment
118                 * @return the builder
119                 */
120                public Builder compartment(String compartment) {
121                        this.myCompartment = compartment;
122                        return this;
123                }
124
125                /**
126                 * Create the jax-rs request
127                 * @return the jax-rs request
128                 */
129                public JaxRsRequest build() {
130         JaxRsRequest result = new JaxRsRequest(myServer, myResource, myRequestType, myRestOperation);
131         if ((StringUtils.isNotBlank(myVersion) || StringUtils.isNotBlank(myCompartment))
132                 && StringUtils.isBlank(myId)) {
133             throw new InvalidRequestException("Don't know how to handle request path: "
134                     + myServer.getUriInfo().getRequestUri().toASCIIString());
135         }
136         
137         FhirVersionEnum fhirContextVersion = myServer.getFhirContext().getVersion().getVersion();
138
139         if (StringUtils.isNotBlank(myVersion)) {
140             if (FhirVersionEnum.DSTU3.equals(fhirContextVersion) || FhirVersionEnum.DSTU2_HL7ORG.equals(fhirContextVersion)) {
141                 result.setId(
142                         new IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
143             } else if (FhirVersionEnum.DSTU2.equals(fhirContextVersion)) {
144                 result.setId(
145                         new IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
146             }
147         } else if (StringUtils.isNotBlank(myId)) {
148             if (FhirVersionEnum.DSTU3.equals(fhirContextVersion) || FhirVersionEnum.DSTU2_HL7ORG.equals(fhirContextVersion)) {
149                 result.setId(new IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
150             } else if (FhirVersionEnum.DSTU2.equals(fhirContextVersion)) {
151                 result.setId(new IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
152             }
153         }
154
155         if (myRestOperation == RestOperationTypeEnum.UPDATE) {
156             String contentLocation = result.getHeader(Constants.HEADER_CONTENT_LOCATION);
157             if (contentLocation != null) {
158                 if (FhirVersionEnum.DSTU3.equals(fhirContextVersion) || FhirVersionEnum.DSTU2_HL7ORG.equals(fhirContextVersion)) {
159                     result.setId(new IdType(contentLocation));
160                 } else if (FhirVersionEnum.DSTU2.equals(fhirContextVersion)) {
161                     result.setId(new IdDt(contentLocation));
162                 }
163             }
164         }
165         
166         result.setCompartmentName(myCompartment);           
167         result.setCompleteUrl(myRequestUrl);
168                        result.setResourceName(myResourceName);
169                        
170                        return result;
171                }
172        }
173
174        private String myResourceString;
175        private HttpHeaders myHeaders;
176        private AbstractJaxRsProvider myServer;
177
178        /**
179         * Utility Constructor
180         * @param server the server
181         * @param resourceString the resource body
182         * @param requestType the request type
183         * @param restOperation the operation type
184         */
185        public JaxRsRequest(AbstractJaxRsProvider server, String resourceString, RequestTypeEnum requestType,
186                        RestOperationTypeEnum restOperation) {
187                this.myHeaders = server.getHeaders();
188                this.myResourceString = resourceString;
189                this.setRestOperationType(restOperation);
190                setServer(server);
191                setFhirServerBase(server.getBaseForServer());
192                setParameters(server.getParameters());
193                setRequestType(requestType);
194        }
195
196        @Override
197        public AbstractJaxRsProvider getServer() {
198                return myServer;
199        }
200
201        /**
202         * Set the server
203         * @param theServer the server to set
204         */
205        public void setServer(AbstractJaxRsProvider theServer) {
206                this.myServer = theServer;
207        }
208
209        @Override
210        public String getHeader(String headerKey) {
211                List<String> requestHeader = getHeaders(headerKey);
212                return requestHeader.isEmpty() ? null : requestHeader.get(0);
213        }
214
215        @Override
216        public List<String> getHeaders(String name) {
217                List<String> requestHeader = myHeaders.getRequestHeader(name);
218                return requestHeader == null ? Collections.<String> emptyList() : requestHeader;
219        }
220
221        @Override
222        public String getServerBaseForRequest() {
223                return getServer().getServerAddressStrategy().determineServerBase(null, null);
224        }
225
226        @Override
227        protected byte[] getByteStreamRequestContents() {
228                return StringUtils.defaultString(myResourceString, "")
229                                .getBytes(ResourceParameter.determineRequestCharset(this));
230        }
231
232        @Override
233        public IRestfulResponse getResponse() {
234                if (super.getResponse() == null) {
235                        setResponse(new JaxRsResponse(this));
236                }
237                return super.getResponse();
238        }
239
240        @Override
241        public Reader getReader() throws IOException {
242                // not yet implemented
243                throw new UnsupportedOperationException();
244        }
245
246        @Override
247        public InputStream getInputStream() {
248                // not yet implemented
249                throw new UnsupportedOperationException();
250        }
251
252        @Override
253        public Charset getCharset() {
254                String charset = null;
255                
256                if(myHeaders.getMediaType() != null && myHeaders.getMediaType().getParameters() != null) {
257                        charset = myHeaders.getMediaType().getParameters().get(MediaType.CHARSET_PARAMETER);
258                }
259                if(charset != null) {
260                        return Charset.forName(charset);
261                } else {
262                        return null;
263                }
264        }
265}