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.BundleInclusionRule;
024import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
025import ca.uhn.fhir.jaxrs.server.util.JaxRsMethodBindings;
026import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
027import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder;
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.IBundleProvider;
032import ca.uhn.fhir.rest.api.server.IRestfulServer;
033import ca.uhn.fhir.rest.server.IPagingProvider;
034import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
035import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
036import jakarta.interceptor.Interceptors;
037import jakarta.ws.rs.Consumes;
038import jakarta.ws.rs.GET;
039import jakarta.ws.rs.POST;
040import jakarta.ws.rs.Produces;
041import jakarta.ws.rs.core.MediaType;
042import jakarta.ws.rs.core.Response;
043
044import java.io.IOException;
045import java.util.Collections;
046import java.util.List;
047
048/**
049 * This server is the abstract superclass for all bundle providers. It exposes
050 * a large amount of the fhir api functionality using JAXRS
051 *
052 * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
053 */
054@SuppressWarnings("javadoc")
055@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN})
056@Consumes({
057        MediaType.APPLICATION_FORM_URLENCODED,
058        MediaType.APPLICATION_JSON,
059        Constants.CT_FHIR_JSON,
060        Constants.CT_FHIR_XML
061})
062@Interceptors(JaxRsExceptionInterceptor.class)
063public abstract class AbstractJaxRsBundleProvider extends AbstractJaxRsProvider
064                implements IRestfulServer<JaxRsRequest>, IBundleProvider {
065
066        /** the method bindings for this class */
067        private final JaxRsMethodBindings theBindings;
068
069        /**
070         * The default constructor. The method bindings are retrieved from the class
071         * being constructed.
072         */
073        protected AbstractJaxRsBundleProvider() {
074                super();
075                theBindings = JaxRsMethodBindings.getMethodBindings(this, getClass());
076        }
077
078        /**
079         * Provides the ability to specify the {@link FhirContext}.
080         * @param ctx the {@link FhirContext} instance.
081         */
082        protected AbstractJaxRsBundleProvider(final FhirContext ctx) {
083                super(ctx);
084                theBindings = JaxRsMethodBindings.getMethodBindings(this, getClass());
085        }
086
087        /**
088         * This constructor takes in an explicit interface class. This subclass
089         * should be identical to the class being constructed but is given
090         * explicitly in order to avoid issues with proxy classes in a jee
091         * environment.
092         *
093         * @param theProviderClass the interface of the class
094         */
095        protected AbstractJaxRsBundleProvider(final Class<? extends AbstractJaxRsProvider> theProviderClass) {
096                theBindings = JaxRsMethodBindings.getMethodBindings(this, theProviderClass);
097        }
098
099        /**
100         * Create all resources in one transaction
101         *
102         * @param resource the body of the post method containing the bundle of the resources being created in a xml/json form
103         * @return the response
104         * @see <a href="https://www.hl7.org/fhir/http.html#create">https://www.hl7. org/fhir/http.html#create</a>
105         */
106        @POST
107        public Response create(final String resource) throws IOException {
108                return execute(getRequest(RequestTypeEnum.POST, RestOperationTypeEnum.TRANSACTION)
109                                .resource(resource));
110        }
111
112        /**
113         * Search the resource type based on some filter criteria
114         *
115         * @return the response
116         * @see <a href="https://www.hl7.org/fhir/http.html#search">https://www.hl7.org/fhir/http.html#search</a>
117         */
118        @GET
119        public Response search() throws IOException {
120                return execute(getRequest(RequestTypeEnum.GET, RestOperationTypeEnum.SEARCH_TYPE));
121        }
122
123        /**
124         * Execute the method described by the requestBuilder and methodKey
125         *
126         * @param theRequestBuilder the requestBuilder that contains the information about the request
127         * @param methodKey the key determining the method to be executed
128         * @return the response
129         */
130        private Response execute(final Builder theRequestBuilder, final String methodKey) throws IOException {
131                final JaxRsRequest theRequest = theRequestBuilder.build();
132                final BaseMethodBinding method = getBinding(theRequest.getRestOperationType(), methodKey);
133                try {
134                        return (Response) method.invokeServer(this, theRequest);
135                } catch (final Throwable theException) {
136                        return handleException(theRequest, theException);
137                }
138        }
139
140        /**
141         * Execute the method described by the requestBuilder
142         *
143         * @param theRequestBuilder the requestBuilder that contains the information about the request
144         * @return the response
145         */
146        private Response execute(final Builder theRequestBuilder) throws IOException {
147                return execute(theRequestBuilder, JaxRsMethodBindings.DEFAULT_METHOD_KEY);
148        }
149
150        /**
151         * Return the method binding for the given rest operation
152         *
153         * @param restOperation the rest operation to retrieve
154         * @param theBindingKey the key determining the method to be executed (needed for e.g. custom operation)
155         * @return
156         */
157        protected BaseMethodBinding getBinding(final RestOperationTypeEnum restOperation, final String theBindingKey) {
158                return getBindings().getBinding(restOperation, theBindingKey);
159        }
160
161        /**
162         * Default: an empty list of interceptors
163         *
164         * @see ca.uhn.fhir.rest.server.IRestfulServerDefaults#getInterceptors_()
165         */
166        @Override
167        public List<IServerInterceptor> getInterceptors_() {
168                return Collections.emptyList();
169        }
170
171        /**
172         * Default: no paging provider
173         */
174        @Override
175        public IPagingProvider getPagingProvider() {
176                return null;
177        }
178
179        /**
180         * Default: BundleInclusionRule.BASED_ON_INCLUDES
181         */
182        @Override
183        public BundleInclusionRule getBundleInclusionRule() {
184                return BundleInclusionRule.BASED_ON_INCLUDES;
185        }
186
187        /**
188         * Return the bindings defined in this resource provider
189         *
190         * @return the jax-rs method bindings
191         */
192        public JaxRsMethodBindings getBindings() {
193                return theBindings;
194        }
195}