001/*-
002 * #%L
003 * HAPI FHIR - Client Framework
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.rest.client.interceptor;
021
022import ca.uhn.fhir.rest.client.api.IClientInterceptor;
023import ca.uhn.fhir.rest.client.api.IHttpRequest;
024import ca.uhn.fhir.rest.client.api.IHttpResponse;
025
026/**
027 * This is a client interceptor that captures the current request and response
028 * in a ThreadLocal variable, meaning that it can work in multithreaded
029 * environments without mixing up requests.
030 * <p>
031 * Use this with caution, since <b>this interceptor does not automatically clean up</b>
032 * the ThreadLocal after setting it. You must make sure to call
033 * {@link #clearThreadLocals()} after a given request has been completed,
034 * or you will end up leaving stale request/response objects associated
035 * with threads that no longer need them.
036 * </p>
037 *
038 * @see CapturingInterceptor for an equivalent interceptor that does not use a ThreadLocal
039 * @since 3.5.0
040 */
041public class ThreadLocalCapturingInterceptor implements IClientInterceptor {
042
043        private final ThreadLocal<IHttpRequest> myRequestThreadLocal = new ThreadLocal<>();
044        private final ThreadLocal<IHttpResponse> myResponseThreadLocal = new ThreadLocal<>();
045        private boolean myBufferResponse;
046
047        /**
048         * This method should be called at the end of any request process, in
049         * order to clear the last request and response from the current thread.
050         */
051        public void clearThreadLocals() {
052                myRequestThreadLocal.remove();
053                myResponseThreadLocal.remove();
054        }
055
056        public IHttpRequest getRequestForCurrentThread() {
057                return myRequestThreadLocal.get();
058        }
059
060        public IHttpResponse getResponseForCurrentThread() {
061                return myResponseThreadLocal.get();
062        }
063
064        @Override
065        public void interceptRequest(IHttpRequest theRequest) {
066                myRequestThreadLocal.set(theRequest);
067        }
068
069        @Override
070        public void interceptResponse(IHttpResponse theResponse) {
071                if (isBufferResponse()) {
072                        CapturingInterceptor.bufferResponse(theResponse);
073                }
074                myResponseThreadLocal.set(theResponse);
075        }
076
077        /**
078         * Should we buffer (capture) the response body? This defaults to
079         * <code>false</code>. Set to <code>true</code> if you are planning on
080         * examining response bodies after the response processing is complete.
081         */
082        public boolean isBufferResponse() {
083                return myBufferResponse;
084        }
085
086        /**
087         * Should we buffer (capture) the response body? This defaults to
088         * <code>false</code>. Set to <code>true</code> if you are planning on
089         * examining response bodies after the response processing is complete.
090         */
091        public ThreadLocalCapturingInterceptor setBufferResponse(boolean theBufferResponse) {
092                myBufferResponse = theBufferResponse;
093                return this;
094        }
095}