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