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.AddProfileTagEnum; 024import ca.uhn.fhir.interceptor.api.IInterceptorService; 025import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor; 026import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsResponseException; 027import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest; 028import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder; 029import ca.uhn.fhir.rest.api.*; 030import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; 031import ca.uhn.fhir.rest.server.*; 032import jakarta.ws.rs.core.Context; 033import jakarta.ws.rs.core.HttpHeaders; 034import jakarta.ws.rs.core.MultivaluedMap; 035import jakarta.ws.rs.core.Response; 036import jakarta.ws.rs.core.UriInfo; 037import org.apache.commons.lang3.StringUtils; 038 039import java.io.IOException; 040import java.util.Map.Entry; 041import java.util.*; 042 043/** 044 * This is the abstract superclass for all jaxrs providers. It contains some defaults implementing 045 * the IRestfulServerDefaults interface and exposes the uri and headers. 046 * 047 * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare 048 */ 049public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults { 050 051 private static final String ERROR = "error"; 052 053 private static final String PROCESSING = "processing"; 054 055 private final FhirContext CTX; 056 /** the http headers */ 057 @Context 058 private HttpHeaders myHeaders; 059 060 /** the uri info */ 061 @Context 062 private UriInfo myUriInfo; 063 064 /** 065 * Default is DSTU2. Use {@link AbstractJaxRsProvider#AbstractJaxRsProvider(FhirContext)} to specify a DSTU3 context. 066 */ 067 protected AbstractJaxRsProvider() { 068 CTX = FhirContext.forDstu2(); 069 } 070 071 /** 072 * 073 * @param ctx 074 * the {@link FhirContext} to support. 075 */ 076 protected AbstractJaxRsProvider(final FhirContext ctx) { 077 CTX = ctx; 078 } 079 080 @Override 081 public IInterceptorService getInterceptorService() { 082 return null; 083 } 084 085 /** 086 * DEFAULT = AddProfileTagEnum.NEVER 087 */ 088 @Override 089 public AddProfileTagEnum getAddProfileTag() { 090 return AddProfileTagEnum.NEVER; 091 } 092 093 /** 094 * This method returns the server base, including the resource path. 095 * {@link UriInfo#getBaseUri() UriInfo#getBaseUri()} 096 * 097 * @return the ascii string for the base resource provider path 098 */ 099 public String getBaseForRequest() { 100 return getBaseForServer(); 101 } 102 103 /** 104 * This method returns the server base, independent of the request or resource. 105 * 106 * @see jakarta.ws.rs.core.UriInfo#getBaseUri() 107 * @return the ascii string for the server base 108 */ 109 public String getBaseForServer() { 110 final String url = getUriInfo().getBaseUri().toASCIIString(); 111 return StringUtils.isNotBlank(url) && url.endsWith("/") ? url.substring(0, url.length() - 1) : url; 112 } 113 114 /** 115 * DEFAULT = EncodingEnum.JSON 116 */ 117 @Override 118 public EncodingEnum getDefaultResponseEncoding() { 119 return EncodingEnum.JSON; 120 } 121 122 /** 123 * DEFAULT = ETagSupportEnum.DISABLED 124 */ 125 @Override 126 public ETagSupportEnum getETagSupport() { 127 return ETagSupportEnum.DISABLED; 128 } 129 130 /** 131 * DEFAULT = {@link ElementsSupportEnum#STANDARD} 132 */ 133 @Override 134 public ElementsSupportEnum getElementsSupport() { 135 return ElementsSupportEnum.STANDARD; 136 } 137 138 @Override 139 public FhirContext getFhirContext() { 140 return CTX; 141 } 142 143 /** 144 * Get the headers 145 * 146 * @return the headers 147 */ 148 public HttpHeaders getHeaders() { 149 return this.myHeaders; 150 } 151 152 /** 153 * Default: an empty list of interceptors (Interceptors are not yet supported 154 * in the JAX-RS server). Please get in touch if you'd like to help! 155 * 156 * @see ca.uhn.fhir.rest.server.IRestfulServerDefaults#getInterceptors_() 157 */ 158 @Override 159 public List<IServerInterceptor> getInterceptors_() { 160 return Collections.emptyList(); 161 } 162 163 /** 164 * By default, no paging provider is used 165 */ 166 @Override 167 public IPagingProvider getPagingProvider() { 168 return null; 169 } 170 171 /** 172 * This method returns the query parameters 173 * 174 * @return the query parameters 175 */ 176 public Map<String, String[]> getParameters() { 177 final MultivaluedMap<String, String> queryParameters = getUriInfo().getQueryParameters(); 178 final HashMap<String, String[]> params = new HashMap<String, String[]>(); 179 for (final Entry<String, List<String>> paramEntry : queryParameters.entrySet()) { 180 params.put( 181 paramEntry.getKey(), 182 paramEntry 183 .getValue() 184 .toArray(new String[paramEntry.getValue().size()])); 185 } 186 return params; 187 } 188 189 /** 190 * Return the requestbuilder for the server 191 * 192 * @param requestType 193 * the type of the request 194 * @param restOperation 195 * the rest operation type 196 * @return the requestbuilder 197 */ 198 public Builder getRequest(final RequestTypeEnum requestType, final RestOperationTypeEnum restOperation) { 199 return getRequest(requestType, restOperation, null); 200 } 201 202 /** 203 * Return the requestbuilder for the server 204 * 205 * @param requestType 206 * the type of the request 207 * @param restOperation 208 * the rest operation type 209 * @param theResourceName 210 * the resource name 211 * @return the requestbuilder 212 */ 213 public Builder getRequest( 214 final RequestTypeEnum requestType, 215 final RestOperationTypeEnum restOperation, 216 final String theResourceName) { 217 return new JaxRsRequest.Builder( 218 this, requestType, restOperation, myUriInfo.getRequestUri().toString(), theResourceName); 219 } 220 221 /** 222 * This method returns the default server address strategy. The default strategy return the 223 * base uri for the request {@link AbstractJaxRsProvider#getBaseForRequest() getBaseForRequest()} 224 * 225 * @return 226 */ 227 public IServerAddressStrategy getServerAddressStrategy() { 228 final HardcodedServerAddressStrategy addressStrategy = new HardcodedServerAddressStrategy(); 229 addressStrategy.setValue(getBaseForRequest()); 230 return addressStrategy; 231 } 232 233 /** 234 * Get the uriInfo 235 * 236 * @return the uri info 237 */ 238 public UriInfo getUriInfo() { 239 return this.myUriInfo; 240 } 241 242 /** 243 * Convert an exception to a response 244 * 245 * @param theRequest 246 * the incoming request 247 * @param theException 248 * the exception to convert 249 * @return response 250 * @throws IOException 251 */ 252 public Response handleException(final JaxRsRequest theRequest, final Throwable theException) throws IOException { 253 if (theException instanceof JaxRsResponseException) { 254 return new JaxRsExceptionInterceptor() 255 .convertExceptionIntoResponse(theRequest, (JaxRsResponseException) theException); 256 } else { 257 return new JaxRsExceptionInterceptor() 258 .convertExceptionIntoResponse( 259 theRequest, new JaxRsExceptionInterceptor().convertException(this, theException)); 260 } 261 } 262 263 /** 264 * DEFAULT = true 265 */ 266 @Override 267 public boolean isDefaultPrettyPrint() { 268 return true; 269 } 270 271 /** 272 * Set the headers 273 * 274 * @param headers 275 * the headers to set 276 */ 277 public void setHeaders(final HttpHeaders headers) { 278 this.myHeaders = headers; 279 } 280 281 /** 282 * Set the Uri Info 283 * 284 * @param uriInfo 285 * the uri info 286 */ 287 public void setUriInfo(final UriInfo uriInfo) { 288 this.myUriInfo = uriInfo; 289 } 290 291 /** 292 * DEFAULT = false 293 */ 294 public boolean withStackTrace() { 295 return false; 296 } 297}