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}