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