001package ca.uhn.fhir.jaxrs.server;
002
003import java.io.IOException;
004/*
005 * #%L
006 * HAPI FHIR JAX-RS Server
007 * %%
008 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
009 * %%
010 * Licensed under the Apache License, Version 2.0 (the "License");
011 * you may not use this file except in compliance with the License.
012 * You may obtain a copy of the License at
013 *
014 * http://www.apache.org/licenses/LICENSE-2.0
015 *
016 * Unless required by applicable law or agreed to in writing, software
017 * distributed under the License is distributed on an "AS IS" BASIS,
018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
019 * See the License for the specific language governing permissions and
020 * limitations under the License.
021 * #L%
022 */
023import java.util.*;
024import java.util.Map.Entry;
025
026import javax.ws.rs.core.Context;
027import javax.ws.rs.core.HttpHeaders;
028
029import ca.uhn.fhir.interceptor.api.IInterceptorService;
030import javax.ws.rs.core.MultivaluedMap;
031import javax.ws.rs.core.Response;
032import javax.ws.rs.core.UriInfo;
033import org.apache.commons.lang3.StringUtils;
034
035import ca.uhn.fhir.context.FhirContext;
036import ca.uhn.fhir.context.api.AddProfileTagEnum;
037import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
038import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsResponseException;
039import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
040import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder;
041import ca.uhn.fhir.rest.api.*;
042import ca.uhn.fhir.rest.server.*;
043import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
044
045/**
046 * This is the abstract superclass for all jaxrs providers. It contains some defaults implementing
047 * the IRestfulServerDefaults interface and exposes the uri and headers.
048 *
049 * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
050 */
051public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
052
053        private static final String ERROR = "error";
054
055        private static final String PROCESSING = "processing";
056
057        private final FhirContext CTX;
058        /** the http headers */
059        @Context
060        private HttpHeaders myHeaders;
061
062        /** the uri info */
063        @Context
064        private UriInfo myUriInfo;
065
066        /**
067         * Default is DSTU2. Use {@link AbstractJaxRsProvider#AbstractJaxRsProvider(FhirContext)} to specify a DSTU3 context.
068         */
069        protected AbstractJaxRsProvider() {
070                CTX = FhirContext.forDstu2();
071        }
072
073        /**
074         *
075         * @param ctx
076         *           the {@link FhirContext} to support.
077         */
078        protected AbstractJaxRsProvider(final FhirContext ctx) {
079                CTX = ctx;
080        }
081
082        @Override
083        public IInterceptorService getInterceptorService() {
084                return null;
085        }
086
087        /**
088         * DEFAULT = AddProfileTagEnum.NEVER
089         */
090        @Override
091        public AddProfileTagEnum getAddProfileTag() {
092                return AddProfileTagEnum.NEVER;
093        }
094
095        /**
096         * This method returns the server base, including the resource path.
097         * {@link UriInfo#getBaseUri() UriInfo#getBaseUri()}
098         * 
099         * @return the ascii string for the base resource provider path
100         */
101        public String getBaseForRequest() {
102                return getBaseForServer();
103        }
104
105        /**
106         * This method returns the server base, independent of the request or resource.
107         * 
108         * @see javax.ws.rs.core.UriInfo#getBaseUri()
109         * @return the ascii string for the server base
110         */
111        public String getBaseForServer() {
112                final String url = getUriInfo().getBaseUri().toASCIIString();
113                return StringUtils.isNotBlank(url) && url.endsWith("/") ? url.substring(0, url.length() - 1) : url;
114        }
115
116        /**
117         * DEFAULT = EncodingEnum.JSON
118         */
119        @Override
120        public EncodingEnum getDefaultResponseEncoding() {
121                return EncodingEnum.JSON;
122        }
123
124        /**
125         * DEFAULT = ETagSupportEnum.DISABLED
126         */
127        @Override
128        public ETagSupportEnum getETagSupport() {
129                return ETagSupportEnum.DISABLED;
130        }
131
132        /**
133         * DEFAULT = {@link ElementsSupportEnum#STANDARD}
134         */
135        @Override
136        public ElementsSupportEnum getElementsSupport() {
137                return ElementsSupportEnum.STANDARD;
138        }
139
140        @Override
141        public FhirContext getFhirContext() {
142                return CTX;
143        }
144
145        /**
146         * Get the headers
147         * 
148         * @return the headers
149         */
150        public HttpHeaders getHeaders() {
151                return this.myHeaders;
152        }
153
154        /**
155         * Default: an empty list of interceptors (Interceptors are not yet supported
156         * in the JAX-RS server). Please get in touch if you'd like to help!
157         * 
158         * @see ca.uhn.fhir.rest.server.IRestfulServerDefaults#getInterceptors_()
159         */
160        @Override
161        public List<IServerInterceptor> getInterceptors_() {
162                return Collections.emptyList();
163        }
164
165        /**
166         * By default, no paging provider is used
167         */
168        @Override
169        public IPagingProvider getPagingProvider() {
170                return null;
171        }
172
173        /**
174         * This method returns the query parameters
175         * 
176         * @return the query parameters
177         */
178        public Map<String, String[]> getParameters() {
179                final MultivaluedMap<String, String> queryParameters = getUriInfo().getQueryParameters();
180                final HashMap<String, String[]> params = new HashMap<String, String[]>();
181                for (final Entry<String, List<String>> paramEntry : queryParameters.entrySet()) {
182                        params.put(paramEntry.getKey(), paramEntry.getValue().toArray(new String[paramEntry.getValue().size()]));
183                }
184                return params;
185        }
186
187        /**
188         * Return the requestbuilder for the server
189         * 
190         * @param requestType
191         *           the type of the request
192         * @param restOperation
193         *           the rest operation type
194         * @return the requestbuilder
195         */
196        public Builder getRequest(final RequestTypeEnum requestType, final RestOperationTypeEnum restOperation) {
197                return getRequest(requestType, restOperation, null);
198        }
199
200        /**
201         * Return the requestbuilder for the server
202         * 
203         * @param requestType
204         *           the type of the request
205         * @param restOperation
206         *           the rest operation type
207         * @param theResourceName
208         *           the resource name
209         * @return the requestbuilder
210         */
211        public Builder getRequest(final RequestTypeEnum requestType, final RestOperationTypeEnum restOperation, final String theResourceName) {
212                return new JaxRsRequest.Builder(this, requestType, restOperation, myUriInfo.getRequestUri().toString(), theResourceName);
213        }
214
215        /**
216         * This method returns the default server address strategy. The default strategy return the
217         * base uri for the request {@link AbstractJaxRsProvider#getBaseForRequest() getBaseForRequest()}
218         * 
219         * @return
220         */
221        public IServerAddressStrategy getServerAddressStrategy() {
222                final HardcodedServerAddressStrategy addressStrategy = new HardcodedServerAddressStrategy();
223                addressStrategy.setValue(getBaseForRequest());
224                return addressStrategy;
225        }
226
227        /**
228         * Get the uriInfo
229         * 
230         * @return the uri info
231         */
232        public UriInfo getUriInfo() {
233                return this.myUriInfo;
234        }
235
236        /**
237         * Convert an exception to a response
238         * 
239         * @param theRequest
240         *           the incoming request
241         * @param theException
242         *           the exception to convert
243         * @return response
244         * @throws IOException
245         */
246        public Response handleException(final JaxRsRequest theRequest, final Throwable theException)
247                        throws IOException {
248                if (theException instanceof JaxRsResponseException) {
249                        return new JaxRsExceptionInterceptor().convertExceptionIntoResponse(theRequest, (JaxRsResponseException) theException);
250                } else {
251                        return new JaxRsExceptionInterceptor().convertExceptionIntoResponse(theRequest,
252                                        new JaxRsExceptionInterceptor().convertException(this, theException));
253                }
254        }
255
256        /**
257         * DEFAULT = true
258         */
259        @Override
260        public boolean isDefaultPrettyPrint() {
261                return true;
262        }
263
264        /**
265         * Set the headers
266         * 
267         * @param headers
268         *           the headers to set
269         */
270        public void setHeaders(final HttpHeaders headers) {
271                this.myHeaders = headers;
272        }
273
274        /**
275         * Set the Uri Info
276         * 
277         * @param uriInfo
278         *           the uri info
279         */
280        public void setUriInfo(final UriInfo uriInfo) {
281                this.myUriInfo = uriInfo;
282        }
283
284        /**
285         * DEFAULT = false
286         */
287        public boolean withStackTrace() {
288                return false;
289        }
290}