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