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.interceptor; 021 022import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider; 023import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest; 024import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; 025import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 026import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor; 027import jakarta.interceptor.AroundInvoke; 028import jakarta.interceptor.InvocationContext; 029import jakarta.servlet.ServletException; 030import jakarta.ws.rs.core.Response; 031 032import java.io.IOException; 033 034/** 035 * An interceptor that catches the jax-rs exceptions 036 * 037 * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare 038 */ 039public class JaxRsExceptionInterceptor { 040 041 /** the existing exception handler which is able to convert exception into responses*/ 042 private final ExceptionHandlingInterceptor exceptionHandler; 043 044 /** 045 * The default constructor 046 */ 047 public JaxRsExceptionInterceptor() { 048 this.exceptionHandler = new ExceptionHandlingInterceptor(); 049 } 050 051 /** 052 * A utility constructor for unit testing 053 * @param exceptionHandler the handler for the exception conversion 054 */ 055 JaxRsExceptionInterceptor(final ExceptionHandlingInterceptor exceptionHandler) { 056 this.exceptionHandler = exceptionHandler; 057 } 058 059 /** 060 * This interceptor will catch all exception and convert them using the exceptionhandler 061 * @param ctx the invocation context 062 * @return the result 063 * @throws JaxRsResponseException an exception that can be handled by a jee container 064 */ 065 @AroundInvoke 066 public Object intercept(final InvocationContext ctx) throws JaxRsResponseException { 067 try { 068 return ctx.proceed(); 069 } catch (final Exception theException) { 070 final AbstractJaxRsProvider theServer = (AbstractJaxRsProvider) ctx.getTarget(); 071 throw convertException(theServer, theException); 072 } 073 } 074 075 /** 076 * This method convert an exception to a JaxRsResponseException 077 * @param theServer the provider 078 * @param theException the exception to convert 079 * @return JaxRsResponseException 080 */ 081 public JaxRsResponseException convertException( 082 final AbstractJaxRsProvider theServer, final Throwable theException) { 083 if (theServer.withStackTrace()) { 084 exceptionHandler.setReturnStackTracesForExceptionTypes(Throwable.class); 085 } 086 final JaxRsRequest requestDetails = theServer.getRequest(null, null).build(); 087 final BaseServerResponseException convertedException = preprocessException(theException, requestDetails); 088 return new JaxRsResponseException(convertedException); 089 } 090 091 /** 092 * This method converts an exception into a response 093 * @param theRequest the request 094 * @param theException the thrown exception 095 * @return the response describing the error 096 * @throws IOException 097 */ 098 public Response convertExceptionIntoResponse( 099 final JaxRsRequest theRequest, final JaxRsResponseException theException) throws IOException { 100 return handleExceptionWithoutServletError(theRequest, theException); 101 } 102 103 private BaseServerResponseException preprocessException( 104 final Throwable theException, final JaxRsRequest requestDetails) { 105 try { 106 Throwable theExceptionToConvert = theException; 107 if (!(theException instanceof BaseServerResponseException) 108 && (theException.getCause() instanceof BaseServerResponseException)) { 109 theExceptionToConvert = theException.getCause(); 110 } 111 return exceptionHandler.preProcessOutgoingException(requestDetails, theExceptionToConvert, null); 112 } catch (final ServletException e) { 113 return new InternalErrorException(e); 114 } 115 } 116 117 private Response handleExceptionWithoutServletError( 118 final JaxRsRequest theRequest, final BaseServerResponseException theException) throws IOException { 119 try { 120 return (Response) exceptionHandler.handleException(theRequest, theException); 121 } catch (final ServletException e) { 122 final BaseServerResponseException newException = 123 preprocessException(new InternalErrorException(e), theRequest); 124 return handleExceptionWithoutServletError(theRequest, newException); 125 } 126 } 127}