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