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