/**
 * Copyright 2012 Netflix, Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.netflix.hystrix.strategy.concurrency;

import java.util.concurrent.ConcurrentHashMap;

import com.netflix.hystrix.HystrixCollapser;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixRequestCache;
import com.netflix.hystrix.HystrixRequestLog;

/**
 * Contains the state and manages the lifecycle of {@link HystrixRequestVariableDefault} objects that provide request scoped (rather than only thread scoped) variables so that multiple threads within
 * a
 * single request can share state:
 * <ul>
 * <li>request scoped caching as in {@link HystrixRequestCache} for de-duping {@link HystrixCommand} executions</li>
 * <li>request scoped log of all events as in {@link HystrixRequestLog}</li>
 * <li>automated batching of {@link HystrixCommand} executions within the scope of a request as in {@link HystrixCollapser}</li>
 * </ul>
 * <p>
 * If those features are not used then this does not need to be used. If those features are used then this must be initialized or a custom implementation of {@link HystrixRequestVariable} must be
 * returned from {@link HystrixConcurrencyStrategy#getRequestVariable}.
 * <p>
 * If {@link HystrixRequestVariableDefault} is used (directly or indirectly by above-mentioned features) and this context has not been initialized then an {@link IllegalStateException} will be thrown
 * with a
 * message such as: <blockquote> HystrixRequestContext.initializeContext() must be called at the beginning of each request before RequestVariable functionality can be used. </blockquote>
 * <p>
 * Example ServletFilter for initializing {@link HystrixRequestContext} at the beginning of an HTTP request and shutting down at the end.
 * 
 * <blockquote>
 * 
 * <pre>
 * public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
 * HystrixRequestContext context = HystrixRequestContext.initializeContext();
 * try {
 * chain.doFilter(request, response);
 * } finally {
 * context.shutdown();
 * }
 * }
 * </pre>
 * 
 * </blockquote>
 * 
 * <p>
 * <b>NOTE:</b> If <code>initializeContext()</code> is called then <code>shutdown()</code> must also be called or a memory leak will occur.
 */
public class HystrixRequestContext {

    /*
     * ThreadLocal on each thread will hold the HystrixRequestVariableState.
     * 
     * Shutdown will clear the state inside HystrixRequestContext but not nullify the ThreadLocal on all
     * child threads as these threads will not be known by the parent when cleanupAfterRequest() is called.
     * 
     * However, the only thing held by those child threads until they are re-used and re-initialized is an empty
     * HystrixRequestContext object with the ConcurrentHashMap within it nulled out since once it is nullified
     * from the parent thread it is shared across all child threads.
     */
    private static ThreadLocal<HystrixRequestContext> requestVariables = new ThreadLocal<HystrixRequestContext>();

    public static boolean isCurrentThreadInitialized() {
        HystrixRequestContext context = requestVariables.get();
        return context != null && context.state != null;
    }

    public static HystrixRequestContext getContextForCurrentThread() {
        HystrixRequestContext context = requestVariables.get();
        if (context != null && context.state != null) {
            // context.state can be null when context is not null
            // if a thread is being re-used and held a context previously, the context was shut down
            // but the thread was not cleared
            return context;
        } else {
            return null;
        }
    }

    public static void setContextOnCurrentThread(HystrixRequestContext state) {
        requestVariables.set(state);
    }

    /**
     * Call this at the beginning of each request (from parent thread)
     * to initialize the underlying context so that {@link HystrixRequestVariableDefault} can be used on any children threads and be accessible from
     * the parent thread.
     * <p>
     * <b>NOTE: If this method is called then <code>shutdown()</code> must also be called or a memory leak will occur.</b>
     * <p>
     * See class header JavaDoc for example Servlet Filter implementation that initializes and shuts down the context.
     */
    public static HystrixRequestContext initializeContext() {
        HystrixRequestContext state = new HystrixRequestContext();
        requestVariables.set(state);
        return state;
    }

    /*
     * This ConcurrentHashMap should not be made publicly accessible. It is the state of RequestVariables for a given RequestContext.
     * 
     * Only HystrixRequestVariable has a reason to be accessing this field.
     */
    /* package */ConcurrentHashMap<HystrixRequestVariableDefault<?>, HystrixRequestVariableDefault.LazyInitializer<?>> state = new ConcurrentHashMap<HystrixRequestVariableDefault<?>, HystrixRequestVariableDefault.LazyInitializer<?>>();

    // instantiation should occur via static factory methods.
    private HystrixRequestContext() {

    }

    /**
     * Shutdown {@link HystrixRequestVariableDefault} objects in this context.
     * <p>
     * <b>NOTE: This must be called if <code>initializeContext()</code> was called or a memory leak will occur.</b>
     */
    public void shutdown() {
        if (state != null) {
            for (HystrixRequestVariableDefault<?> v : state.keySet()) {
                // for each RequestVariable we call 'remove' which performs the shutdown logic
                try {
                    HystrixRequestVariableDefault.remove(this, v);
                } catch (Throwable t) {
                    HystrixRequestVariableDefault.logger.error("Error in shutdown, will continue with shutdown of other variables", t);
                }
            }
            // null out so it can be garbage collected even if the containing object is still
            // being held in ThreadLocals on threads that weren't cleaned up
            state = null;
        }
    };
}
@@ -2278,17 +2278,17 @@
  the end
-.
+:
 %0A * %0A * 
@@ -2447,16 +2447,21 @@
 ion %7B%0A *
+     
  Hystrix
@@ -2530,16 +2530,21 @@
 xt();%0A *
+     
  try %7B%0A 
@@ -2544,16 +2544,26 @@
 try %7B%0A *
+          
  chain.d
@@ -2592,16 +2592,21 @@
 nse);%0A *
+     
  %7D final
@@ -2612,16 +2612,26 @@
 lly %7B%0A *
+          
  context
@@ -2645,16 +2645,21 @@
 wn();%0A *
+     
  %7D%0A * %7D%0A
@@ -2688,24 +2688,216 @@
 ckquote%3E%0A * 
+%3Cp%3E%0A * You can find an implementation at %3Ca href=%22https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-request-servlet%22%3Ehystrix-contrib/hystrix-request-servlet%3C/a%3E on GitHub.
 %0A * %3Cp%3E%0A * %3C

Add link from Javadoc to hystrix-contrib/hystrix-request-servlet