001package ca.uhn.fhir.jaxrs.server;
002
003/*
004 * #%L
005 * HAPI FHIR JAX-RS Server
006 * %%
007 * Copyright (C) 2014 - 2016 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.Consumes;
029import javax.ws.rs.GET;
030import javax.ws.rs.POST;
031import javax.ws.rs.Produces;
032import javax.ws.rs.core.MediaType;
033import javax.ws.rs.core.Response;
034
035import ca.uhn.fhir.context.FhirContext;
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.RequestTypeEnum;
041import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
042import ca.uhn.fhir.rest.method.BaseMethodBinding;
043import ca.uhn.fhir.rest.server.BundleInclusionRule;
044import ca.uhn.fhir.rest.server.Constants;
045import ca.uhn.fhir.rest.server.IBundleProvider;
046import ca.uhn.fhir.rest.server.IPagingProvider;
047import ca.uhn.fhir.rest.server.IRestfulServer;
048import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
049
050/**
051 * This server is the abstract superclass for all bundle providers. It exposes
052 * a large amount of the fhir api functionality using JAXRS
053 *
054 * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
055 */
056@SuppressWarnings("javadoc")
057@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
058@Consumes({ MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
059@Interceptors(JaxRsExceptionInterceptor.class)
060public abstract class AbstractJaxRsBundleProvider extends AbstractJaxRsProvider implements IRestfulServer<JaxRsRequest>, IBundleProvider {
061
062    /** the method bindings for this class */
063    private final JaxRsMethodBindings theBindings;
064
065    /**
066     * The default constructor. The method bindings are retrieved from the class
067     * being constructed.
068     */
069    protected AbstractJaxRsBundleProvider() {
070        super();
071        theBindings = JaxRsMethodBindings.getMethodBindings(this, getClass());
072    }
073
074    /**
075     * Provides the ability to specify the {@link FhirContext}.
076     * @param ctx the {@link FhirContext} instance.
077     */
078    protected AbstractJaxRsBundleProvider(final FhirContext ctx) {
079        super(ctx);
080        theBindings = JaxRsMethodBindings.getMethodBindings(this, getClass());
081    }
082
083    /**
084     * This constructor takes in an explicit interface class. This subclass
085     * should be identical to the class being constructed but is given
086     * explicitly in order to avoid issues with proxy classes in a jee
087     * environment.
088     *
089     * @param theProviderClass the interface of the class
090     */
091    protected AbstractJaxRsBundleProvider(final Class<? extends AbstractJaxRsProvider> theProviderClass) {
092        theBindings = JaxRsMethodBindings.getMethodBindings(this, theProviderClass);
093    }
094
095    /**
096     * Create all resources in one transaction
097     *
098     * @param resource the body of the post method containing the bundle of the resources being created in a xml/json form
099     * @return the response
100     * @see <a href="https://www.hl7.org/fhir/http.html#create">https://www.hl7. org/fhir/http.html#create</a>
101     */
102    @POST
103    public Response create(final String resource)
104            throws IOException {
105        return execute(getRequest(RequestTypeEnum.POST, RestOperationTypeEnum.TRANSACTION).resource(resource));
106    }
107
108    /**
109     * Search the resource type based on some filter criteria
110     *
111     * @return the response
112     * @see <a href="https://www.hl7.org/fhir/http.html#search">https://www.hl7.org/fhir/http.html#search</a>
113     */
114    @GET
115    public Response search()
116            throws IOException {
117        return execute(getRequest(RequestTypeEnum.GET, RestOperationTypeEnum.SEARCH_TYPE));
118    }
119
120    /**
121     * Execute the method described by the requestBuilder and methodKey
122     *
123     * @param theRequestBuilder the requestBuilder that contains the information about the request
124     * @param methodKey the key determining the method to be executed
125     * @return the response
126     */
127    private Response execute(final Builder theRequestBuilder, final String methodKey)
128            throws IOException {
129        final JaxRsRequest theRequest = theRequestBuilder.build();
130        final BaseMethodBinding<?> method = getBinding(theRequest.getRestOperationType(), methodKey);
131        try {
132            return (Response) method.invokeServer(this, theRequest);
133        }
134        catch (final Throwable theException) {
135            return handleException(theRequest, theException);
136        }
137    }
138
139    /**
140     * Execute the method described by the requestBuilder
141     *
142     * @param theRequestBuilder the requestBuilder that contains the information about the request
143     * @return the response
144     */
145    private Response execute(final Builder theRequestBuilder)
146            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.IRestfulServer#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
196}