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