You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2015/07/30 17:33:51 UTC

svn commit: r1693442 - in /lucene/dev/branches/branch_5x: ./ solr/ solr/core/ solr/core/src/java/org/apache/solr/request/ solr/core/src/java/org/apache/solr/search/ solr/core/src/java/org/apache/solr/servlet/ solr/core/src/java/org/apache/solr/util/ so...

Author: noble
Date: Thu Jul 30 15:33:50 2015
New Revision: 1693442

URL: http://svn.apache.org/r1693442
Log:
SOLR-6625: Enable registering interceptors for the calls made using HttpClient and make the request object available at the interceptor context

Added:
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java
      - copied, changed from r1693432, lucene/dev/trunk/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/util/SolrHttpClient.java
      - copied unchanged from r1693432, lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/SolrHttpClient.java
    lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrHttpContext.java
      - copied unchanged from r1693432, lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrHttpContext.java
Modified:
    lucene/dev/branches/branch_5x/   (props changed)
    lucene/dev/branches/branch_5x/solr/   (props changed)
    lucene/dev/branches/branch_5x/solr/CHANGES.txt   (contents, props changed)
    lucene/dev/branches/branch_5x/solr/core/   (props changed)
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/search/   (props changed)
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
    lucene/dev/branches/branch_5x/solr/solrj/   (props changed)
    lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientConfigurer.java
    lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientUtil.java
    lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java
    lucene/dev/branches/branch_5x/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java
    lucene/dev/branches/branch_5x/solr/test-framework/   (props changed)
    lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java
    lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java
    lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java

Modified: lucene/dev/branches/branch_5x/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/CHANGES.txt?rev=1693442&r1=1693441&r2=1693442&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/CHANGES.txt (original)
+++ lucene/dev/branches/branch_5x/solr/CHANGES.txt Thu Jul 30 15:33:50 2015
@@ -312,6 +312,9 @@ Other Changes
   server/solr-webapp, solr.war is no longer included in the distribution
   bundles. (Timothy Potter, Uwe Schindler)
 
+* SOLR-6625: Enable registering interceptors for the calls made using HttpClient and make the
+  request object available at the interceptor context ( Ishan Chattopadhyaya, Gregory Chanan, noble, Anshum Gupta)
+
 ==================  5.2.1 ==================
 
 Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this release

Copied: lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java (from r1693432, lucene/dev/trunk/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java)
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java?p2=lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java&p1=lucene/dev/trunk/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java&r1=1693432&r2=1693442&rev=1693442&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java (original)
+++ lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java Thu Jul 30 15:33:50 2015
@@ -26,19 +26,19 @@ import org.apache.solr.client.solrj.impl
  */
 public class SolrQueryRequestContext extends SolrHttpContext {
   final private SolrQueryRequest solrQueryRequest;
-  
+
   public SolrQueryRequestContext(SolrQueryRequest solrQueryRequest) {
     this.solrQueryRequest = solrQueryRequest;
     setAttribute(SolrHttpContext.class.getName(), this);
   }
-  
+
   public SolrQueryRequest getSolrQueryRequest() {
     return solrQueryRequest;
   }
-  
+
   @Override
   public String toString() {
-    return "[SolrQueryRequestContext contains: "+solrQueryRequest+"]";
+    return "[SolrQueryRequestContext contains: " + solrQueryRequest + "]";
   }
 }
 

Modified: lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java?rev=1693442&r1=1693441&r2=1693442&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java (original)
+++ lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java Thu Jul 30 15:33:50 2015
@@ -17,19 +17,21 @@
 
 package org.apache.solr.request;
 
+import java.io.Closeable;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.concurrent.atomic.AtomicReference;
+
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.util.ExecutorUtil;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.handler.component.ResponseBuilder;
 import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.util.TimeZoneUtils;
 
-import java.io.Closeable;
-import java.util.Date;
-import java.util.TimeZone;
-import java.util.LinkedList;
-import java.util.List;
-
 
 public class SolrRequestInfo {
   protected final static ThreadLocal<SolrRequestInfo> threadLocal = new ThreadLocal<>();
@@ -50,7 +52,8 @@ public class SolrRequestInfo {
     // TODO: temporary sanity check... this can be changed to just an assert in the future
     SolrRequestInfo prev = threadLocal.get();
     if (prev != null) {
-      SolrCore.log.error("Previous SolrRequestInfo was not closed!  req=" + prev.req.getOriginalParams().toString());  
+      SolrCore.log.error("Previous SolrRequestInfo was not closed!  req=" + prev.req.getOriginalParams().toString());
+      SolrCore.log.error("prev == info : {}", prev.req == info.req);
     }
     assert prev == null;
 
@@ -137,4 +140,28 @@ public class SolrRequestInfo {
       closeHooks.add(hook);
     }
   }
+
+  public static ExecutorUtil.InheritableThreadLocalProvider getInheritableThreadLocalProvider() {
+    return new ExecutorUtil.InheritableThreadLocalProvider() {
+      @Override
+      public void store(AtomicReference ctx) {
+        SolrRequestInfo me = threadLocal.get();
+        if (me != null) ctx.set(me);
+      }
+
+      @Override
+      public void set(AtomicReference ctx) {
+        SolrRequestInfo me = (SolrRequestInfo) ctx.get();
+        if (me != null) {
+          ctx.set(null);
+          threadLocal.set(me);
+        }
+      }
+
+      @Override
+      public void clean(AtomicReference ctx) {
+        threadLocal.remove();
+      }
+    };
+  }
 }

Modified: lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java?rev=1693442&r1=1693441&r2=1693442&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java (original)
+++ lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java Thu Jul 30 15:33:50 2015
@@ -2189,6 +2189,7 @@ public class SolrIndexSearcher extends I
       };
 
       SolrQueryResponse rsp = new SolrQueryResponse();
+      SolrRequestInfo.clearRequestInfo();
       SolrRequestInfo.setRequestInfo(new SolrRequestInfo(req, rsp));
       try {
         this.cacheList[i].warm(this, old.cacheList[i]);

Modified: lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java?rev=1693442&r1=1693441&r2=1693442&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java (original)
+++ lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java Thu Jul 30 15:33:50 2015
@@ -44,12 +44,15 @@ import org.apache.solr.common.SolrExcept
 import org.apache.solr.common.SolrException.ErrorCode;
 import org.apache.solr.common.cloud.SolrZkClient;
 import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.util.ExecutorUtil;
 import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.NodeConfig;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.core.SolrResourceLoader;
 import org.apache.solr.core.SolrXmlConfig;
+import org.apache.solr.request.SolrRequestInfo;
 import org.apache.solr.security.AuthenticationPlugin;
+import org.apache.solr.util.SolrHttpClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -90,6 +93,9 @@ public class SolrDispatchFilter extends
   public void init(FilterConfig config) throws ServletException
   {
     log.info("SolrDispatchFilter.init(): {}", this.getClass().getClassLoader());
+
+    HttpClientUtil.HttpClientFactory.setHttpClientImpl(SolrHttpClient.SolrDefaultHttpClient.class, SolrHttpClient.SolrSystemDefaultHttpClient.class);
+
     String exclude = config.getInitParameter("excludePatterns");
     if(exclude != null) {
       String[] excludeArray = exclude.split(",");
@@ -106,13 +112,14 @@ public class SolrDispatchFilter extends
       String solrHome = (String) config.getServletContext().getAttribute(SOLRHOME_ATTRIBUTE);
       if (solrHome == null)
         solrHome = SolrResourceLoader.locateSolrHome();
+      ExecutorUtil.addThreadLocalProvider(SolrRequestInfo.getInheritableThreadLocalProvider());
 
       this.cores = createCoreContainer(solrHome, extraProperties);
 
       if (this.cores.getAuthenticationPlugin() != null) {
         HttpClientConfigurer configurer = this.cores.getAuthenticationPlugin().getDefaultConfigurer();
         if (configurer != null) {
-          configurer.configure((DefaultHttpClient)httpClient, new ModifiableSolrParams());
+          configurer.configure((DefaultHttpClient) httpClient, new ModifiableSolrParams());
         }
       }
 

Modified: lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientConfigurer.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientConfigurer.java?rev=1693442&r1=1693441&r2=1693442&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientConfigurer.java (original)
+++ lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientConfigurer.java Thu Jul 30 15:33:50 2015
@@ -18,8 +18,13 @@ package org.apache.solr.client.solrj.imp
  */
 
 
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
 import org.apache.http.conn.ssl.SSLSocketFactory;
 import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.HttpContext;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
 import org.apache.solr.common.params.SolrParams;
 
 /**
@@ -76,6 +81,18 @@ public class HttpClientConfigurer {
     if(sslCheckPeerName == false) {
       HttpClientUtil.setHostNameVerifier(httpClient, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
     }
+
+    // Intercept every request made through httpclient and validate it has a SolrHttpContext object.
+    httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
+      @Override
+      public void process(final HttpRequest request, final HttpContext context) {
+        // Verify that a context object was passed in
+        final Object solrContext = context.getAttribute(SolrHttpContext.SOLR_CONTEXT_KEY);
+        if (solrContext == null || solrContext instanceof SolrHttpContext == false) {
+          throw new SolrException(ErrorCode.BAD_REQUEST, "A SolrHttpContext object must be passed in as context. Context: " + context);
+        }
+      }
+    });
   }
   
   public static boolean toBooleanDefaultIfNull(Boolean bool, boolean valueIfNull) {

Modified: lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientUtil.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientUtil.java?rev=1693442&r1=1693441&r2=1693442&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientUtil.java (original)
+++ lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientUtil.java Thu Jul 30 15:33:50 2015
@@ -18,6 +18,11 @@ package org.apache.solr.client.solrj.imp
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.zip.GZIPInputStream;
 import java.util.zip.InflaterInputStream;
 
@@ -46,6 +51,8 @@ import org.apache.http.impl.conn.Pooling
 import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; // jdoc
 import org.apache.http.params.HttpConnectionParams;
 import org.apache.http.protocol.HttpContext;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.params.SolrParams;
 import org.slf4j.Logger;
@@ -87,8 +94,8 @@ public class HttpClientUtil {
       0, false);
 
   private static HttpClientConfigurer configurer = new HttpClientConfigurer();
-  
-  private HttpClientUtil(){}
+
+  private static final List<HttpRequestInterceptor> interceptors = Collections.synchronizedList(new ArrayList<HttpRequestInterceptor>());
   
   /**
    * Replace the {@link HttpClientConfigurer} class used in configuring the http
@@ -114,7 +121,7 @@ public class HttpClientUtil {
     if (logger.isDebugEnabled()) {
       logger.debug("Creating new http client, config:" + config);
     }
-    final DefaultHttpClient httpClient = new SystemDefaultHttpClient();
+    final DefaultHttpClient httpClient = HttpClientFactory.createHttpClient();
     configureClient(httpClient, config);
     return httpClient;
   }
@@ -128,7 +135,7 @@ public class HttpClientUtil {
     if (logger.isDebugEnabled()) {
       logger.debug("Creating new http client, config:" + config);
     }
-    final DefaultHttpClient httpClient = new DefaultHttpClient(cm);
+    final DefaultHttpClient httpClient = HttpClientFactory.createHttpClient(cm);
     configureClient(httpClient, config);
     return httpClient;
   }
@@ -140,6 +147,11 @@ public class HttpClientUtil {
   public static void configureClient(final DefaultHttpClient httpClient,
       SolrParams config) {
     configurer.configure(httpClient,  config);
+    synchronized(interceptors) {
+      for(HttpRequestInterceptor interceptor: interceptors) {
+        httpClient.addRequestInterceptor(interceptor);
+      }
+    }
   }
   
   public static void close(HttpClient httpClient) { 
@@ -150,6 +162,14 @@ public class HttpClientUtil {
     }
   }
 
+  public static void addRequestInterceptor(HttpRequestInterceptor interceptor) {
+    interceptors.add(interceptor);
+  }
+
+  public static void removeRequestInterceptor(HttpRequestInterceptor interceptor) {
+    interceptors.remove(interceptor);
+  }
+
   /**
    * Control HTTP payload compression.
    * 
@@ -358,5 +378,36 @@ public class HttpClientUtil {
       return new InflaterInputStream(wrappedEntity.getContent());
     }
   }
-  
+
+  public static class HttpClientFactory {
+    private static Class<? extends DefaultHttpClient> defaultHttpClientClass = DefaultHttpClient.class;
+    private static Class<? extends SystemDefaultHttpClient> systemDefaultHttpClientClass = SystemDefaultHttpClient.class;
+
+
+    public static SystemDefaultHttpClient createHttpClient() {
+      Constructor<? extends SystemDefaultHttpClient> constructor;
+      try {
+        constructor = systemDefaultHttpClientClass.getDeclaredConstructor();
+        return constructor.newInstance();
+      } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+        throw new SolrException(ErrorCode.SERVER_ERROR, "Unable to create HttpClient instance. ", e);
+      }
+    }
+
+    public static DefaultHttpClient createHttpClient(ClientConnectionManager cm) {
+      Constructor<? extends DefaultHttpClient> productConstructor;
+      try {
+        productConstructor = defaultHttpClientClass.getDeclaredConstructor(new Class[]{ClientConnectionManager.class});
+        return productConstructor.newInstance(new Object[]{cm});
+      } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+        throw new SolrException(ErrorCode.SERVER_ERROR, "Unable to create HttpClient instance, registered class is: " + defaultHttpClientClass, e);
+      }
+    }
+
+    public static void setHttpClientImpl(Class<? extends DefaultHttpClient> defaultHttpClient, Class<? extends SystemDefaultHttpClient> systemDefaultHttpClient) {
+      defaultHttpClientClass = defaultHttpClient;
+      systemDefaultHttpClientClass = systemDefaultHttpClient;
+    }
+  }
+
 }

Modified: lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java?rev=1693442&r1=1693441&r2=1693442&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java (original)
+++ lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java Thu Jul 30 15:33:50 2015
@@ -1,5 +1,6 @@
 package org.apache.solr.common.util;
 
+import java.util.ArrayList;
 import java.util.Collection;
 
 /*
@@ -19,8 +20,15 @@ import java.util.Collection;
  * limitations under the License.
  */
 
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.RejectedExecutionHandler;
@@ -28,6 +36,7 @@ import java.util.concurrent.SynchronousQ
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -36,7 +45,39 @@ import org.slf4j.MDC;
 
 public class ExecutorUtil {
   public static Logger log = LoggerFactory.getLogger(ExecutorUtil.class);
-  
+
+  private static volatile List<InheritableThreadLocalProvider> providers = new ArrayList<>();
+
+  public synchronized static void addThreadLocalProvider(InheritableThreadLocalProvider provider) {
+    for (InheritableThreadLocalProvider p : providers) {//this is to avoid accidental multiple addition of providers in tests
+      if (p.getClass().equals(provider.getClass())) return;
+    }
+    List<InheritableThreadLocalProvider> copy = new ArrayList<>(providers);
+    copy.add(provider);
+    providers = copy;
+  }
+
+  /** Any class which wants to carry forward the threadlocal values to the threads run
+   * by threadpools must implement this interface and the implementation should be
+   * registered here
+   */
+  public interface InheritableThreadLocalProvider {
+    /**This is invoked in the parent thread which submitted a task.
+     * copy the necessary Objects to the ctx. The object that is passed is same
+     * across all three methods
+     */
+    public void store(AtomicReference<?> ctx);
+
+    /**This is invoked in the Threadpool thread. set the appropriate values in the threadlocal
+     * of this thread.     */
+    public void set(AtomicReference<?> ctx);
+
+    /**This method is invoked in the threadpool thread after the execution
+     * clean all the variables set in the set method
+     */
+    public void clean(AtomicReference<?> ctx);
+  }
+
   // this will interrupt the threads! Lucene and Solr do not like this because it can close channels, so only use
   // this if you know what you are doing - you probably want shutdownAndAwaitTermination
   public static void shutdownNowAndAwaitTermination(ExecutorService pool) {
@@ -128,7 +169,7 @@ public class ExecutorUtil {
       StringBuilder contextString = new StringBuilder();
       if (submitterContext != null) {
         Collection<String> values = submitterContext.values();
-        
+
         for (String value : values) {
           contextString.append(value + " ");
         }
@@ -136,13 +177,25 @@ public class ExecutorUtil {
           contextString.setLength(contextString.length() - 1);
         }
       }
-      
+
       String ctxStr = contextString.toString().replace("/", "//");
       final String submitterContextStr = ctxStr.length() <= MAX_THREAD_NAME_LEN ? ctxStr : ctxStr.substring(0, MAX_THREAD_NAME_LEN);
       final Exception submitterStackTrace = new Exception("Submitter stack trace");
+      final List<InheritableThreadLocalProvider> providersCopy = providers;
+      final ArrayList<AtomicReference> ctx = providersCopy.isEmpty() ? null : new ArrayList<AtomicReference>(providersCopy.size());
+      if (ctx != null) {
+        for (int i = 0; i < providers.size(); i++) {
+          AtomicReference reference = new AtomicReference();
+          ctx.add(reference);
+          providersCopy.get(i).store(reference);
+        }
+      }
       super.execute(new Runnable() {
         @Override
         public void run() {
+          if (ctx != null) {
+            for (int i = 0; i < providersCopy.size(); i++) providersCopy.get(i).set(ctx.get(i));
+          }
           Map<String, String> threadContext = MDC.getCopyOfContextMap();
           final Thread currentThread = Thread.currentThread();
           final String oldName = currentThread.getName();
@@ -155,7 +208,7 @@ public class ExecutorUtil {
           try {
             command.run();
           } catch (Throwable t) {
-            if (t instanceof OutOfMemoryError)  {
+            if (t instanceof OutOfMemoryError) {
               throw t;
             }
             log.error("Uncaught exception {} thrown by thread: {}", t, currentThread.getName(), submitterStackTrace);
@@ -166,6 +219,9 @@ public class ExecutorUtil {
             } else {
               MDC.clear();
             }
+            if (ctx != null) {
+              for (int i = 0; i < providersCopy.size(); i++) providersCopy.get(i).clean(ctx.get(i));
+            }
             currentThread.setName(oldName);
           }
         }

Modified: lucene/dev/branches/branch_5x/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java?rev=1693442&r1=1693441&r2=1693442&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java (original)
+++ lucene/dev/branches/branch_5x/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java Thu Jul 30 15:33:50 2015
@@ -21,23 +21,40 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URISyntaxException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 
 import org.apache.http.Header;
 import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
 import org.apache.http.HttpResponse;
 import org.apache.http.ParseException;
+import org.apache.http.client.CookieStore;
 import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.params.HttpClientParams;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecRegistry;
+import org.apache.http.impl.client.BasicCookieStore;
 import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.RequestWrapper;
+import org.apache.http.impl.cookie.BasicClientCookie;
+import org.apache.http.protocol.HttpContext;
 import org.apache.solr.SolrJettyTestBase;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrRequest;
@@ -57,9 +74,13 @@ import org.apache.solr.util.SSLTestConfi
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class BasicHttpSolrClientTest extends SolrJettyTestBase {
-  
+
+  private static Logger log = LoggerFactory.getLogger(BasicHttpSolrClientTest.class);
+
   public static class RedirectServlet extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp)
@@ -85,6 +106,7 @@ public class BasicHttpSolrClientTest ext
       parameters = null;
       errorCode = null;
       queryString = null;
+      cookies = null;
     }
     
     public static Integer errorCode = null;
@@ -92,12 +114,19 @@ public class BasicHttpSolrClientTest ext
     public static HashMap<String,String> headers = null;
     public static Map<String,String[]> parameters = null;
     public static String queryString = null;
+    public static javax.servlet.http.Cookie[] cookies = null;
     
     public static void setErrorCode(Integer code) {
       errorCode = code;
     }
     
-
+    @Override
+    protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+      lastMethod = "delete";
+      recordRequest(req, resp);
+    }
+    
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp)
         throws ServletException, IOException {
@@ -105,6 +134,13 @@ public class BasicHttpSolrClientTest ext
       recordRequest(req, resp);
     }
     
+    @Override
+    protected void doHead(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+      lastMethod = "head";
+      recordRequest(req, resp);
+    }
+    
     private void setHeaders(HttpServletRequest req) {
       Enumeration<String> headerNames = req.getHeaderNames();
       headers = new HashMap<>();
@@ -123,6 +159,11 @@ public class BasicHttpSolrClientTest ext
       queryString = req.getQueryString();
     }
 
+    private void setCookies(HttpServletRequest req) {
+      javax.servlet.http.Cookie[] ck = req.getCookies();
+      cookies = req.getCookies();
+    }
+
     @Override
     protected void doPost(HttpServletRequest req, HttpServletResponse resp)
         throws ServletException, IOException {
@@ -141,6 +182,7 @@ public class BasicHttpSolrClientTest ext
       setHeaders(req);
       setParameters(req);
       setQueryString(req);
+      setCookies(req);
       if (null != errorCode) {
         try { 
           resp.sendError(errorCode); 
@@ -593,6 +635,97 @@ public class BasicHttpSolrClientTest ext
                   SSLTestConfig.TEST_KEYSTORE);
   }
 
+  /**
+   * An interceptor changing the request
+   */
+  HttpRequestInterceptor changeRequestInterceptor = new HttpRequestInterceptor() {
+
+    @Override
+    public void process(HttpRequest request, HttpContext context) throws HttpException,
+    IOException {
+      log.info("Intercepted params: "+context);
+
+      RequestWrapper wrapper = (RequestWrapper) request;
+      URIBuilder uribuilder = new URIBuilder(wrapper.getURI());
+      uribuilder.addParameter("b", "\u4321");
+      try {
+        wrapper.setURI(uribuilder.build());
+      } catch (URISyntaxException ex) {
+        throw new HttpException("Invalid request URI", ex);
+      }
+    }
+  };
+
+  public static final String cookieName = "cookieName";
+  public static final String cookieValue = "cookieValue";
+
+  /**
+   * An interceptor setting a cookie
+   */
+  HttpRequestInterceptor cookieSettingRequestInterceptor = new HttpRequestInterceptor() {    
+    @Override
+    public void process(HttpRequest request, HttpContext context) throws HttpException,
+    IOException {
+      BasicClientCookie cookie = new BasicClientCookie(cookieName, cookieValue);
+      cookie.setVersion(0);
+      cookie.setPath("/");
+      cookie.setDomain(jetty.getBaseUrl().getHost());
+
+      CookieStore cookieStore = new BasicCookieStore();        
+      CookieSpecRegistry registry = (CookieSpecRegistry) context.getAttribute(ClientContext.COOKIESPEC_REGISTRY);
+      String policy = HttpClientParams.getCookiePolicy(request.getParams());
+      CookieSpec cookieSpec = registry.getCookieSpec(policy, request.getParams());
+      // Add the cookies to the request
+      List<Header> headers = cookieSpec.formatCookies(Collections.singletonList((Cookie)cookie));
+      for (Header header : headers) {
+        request.addHeader(header);
+      }
+      context.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
+      context.setAttribute(ClientContext.COOKIE_SPEC, cookieSpec);
+    }
+  };
+
+
+  /**
+   * Set cookies via interceptor
+   * Change the request via an interceptor
+   * Ensure cookies are actually set and that request is actually changed
+   */
+  @Test
+  public void testInterceptors() {
+    DebugServlet.clear();
+    HttpClientUtil.addRequestInterceptor(changeRequestInterceptor);
+    HttpClientUtil.addRequestInterceptor(cookieSettingRequestInterceptor);    
+
+    try(HttpSolrClient server = new HttpSolrClient(jetty.getBaseUrl().toString() +
+        "/debug/foo")) {
+
+      SolrQuery q = new SolrQuery("foo");
+      q.setParam("a", "\u1234");
+      try {
+        server.query(q, random().nextBoolean()?METHOD.POST:METHOD.GET);
+      } catch (Throwable t) {}
+
+      // Assert cookies from UseContextCallback 
+      assertNotNull(DebugServlet.cookies);
+      boolean foundCookie = false;
+      for (javax.servlet.http.Cookie cookie : DebugServlet.cookies) {
+        if (cookieName.equals(cookie.getName())
+            && cookieValue.equals(cookie.getValue())) {
+          foundCookie = true;
+          break;
+        }
+      }
+      assertTrue(foundCookie);
+
+      // Assert request changes by ChangeRequestCallback
+      assertEquals("\u1234", DebugServlet.parameters.get("a")[0]);
+      assertEquals("\u4321", DebugServlet.parameters.get("b")[0]);
+
+    } catch (IOException ex) {
+      throw new RuntimeException(ex);
+    }
+  }
 
   private Set<String> setOf(String... keys) {
     Set<String> set = new TreeSet<>();

Modified: lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java?rev=1693442&r1=1693441&r2=1693442&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java (original)
+++ lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java Thu Jul 30 15:33:50 2015
@@ -27,6 +27,7 @@ import org.apache.solr.client.solrj.Solr
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.embedded.JettyConfig;
 import org.apache.solr.client.solrj.embedded.JettySolrRunner;
+import org.apache.solr.client.solrj.impl.HttpClientUtil;
 import org.apache.solr.client.solrj.impl.HttpSolrClient;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.response.QueryResponse;
@@ -37,8 +38,8 @@ import org.apache.solr.common.SolrInputD
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.NamedList;
-import org.apache.solr.schema.TrieDateField;
 import org.apache.solr.util.DateFormatUtil;
+import org.apache.solr.util.SolrHttpClient;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
@@ -103,6 +104,8 @@ public abstract class BaseDistributedSea
   public static void initialize() {
     assumeFalse("SOLR-4147: ibm 64bit has jvm bugs!", Constants.JRE_IS_64BIT && Constants.JAVA_VENDOR.startsWith("IBM"));
     r = new Random(random().nextLong());
+
+    HttpClientUtil.HttpClientFactory.setHttpClientImpl(SolrHttpClient.SolrDefaultHttpClient.class, SolrHttpClient.SolrSystemDefaultHttpClient.class);
   }
   
   /**

Modified: lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java?rev=1693442&r1=1693441&r2=1693442&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java (original)
+++ lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java Thu Jul 30 15:33:50 2015
@@ -23,8 +23,10 @@ import org.apache.solr.client.solrj.Solr
 import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
 import org.apache.solr.client.solrj.embedded.JettyConfig;
 import org.apache.solr.client.solrj.embedded.JettySolrRunner;
+import org.apache.solr.client.solrj.impl.HttpClientUtil;
 import org.apache.solr.client.solrj.impl.HttpSolrClient;
 import org.apache.solr.util.ExternalPaths;
+import org.apache.solr.util.SolrHttpClient;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
@@ -42,6 +44,9 @@ abstract public class SolrJettyTestBase
 {
   private static Logger log = LoggerFactory.getLogger(SolrJettyTestBase.class);
 
+  static {
+    HttpClientUtil.HttpClientFactory.setHttpClientImpl(SolrHttpClient.SolrDefaultHttpClient.class, SolrHttpClient.SolrSystemDefaultHttpClient.class);
+  }
 
   @BeforeClass
   public static void beforeSolrJettyTestBase() throws Exception {

Modified: lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java?rev=1693442&r1=1693441&r2=1693442&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java (original)
+++ lucene/dev/branches/branch_5x/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java Thu Jul 30 15:33:50 2015
@@ -16,6 +16,7 @@ package org.apache.solr.util;
  * limitations under the License.
  */
 
+import org.apache.solr.client.solrj.impl.HttpClientUtil;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.util.XML;
 import org.w3c.dom.Document;
@@ -39,6 +40,10 @@ abstract public class BaseTestHarness {
   private static final ThreadLocal<DocumentBuilder> builderTL = new ThreadLocal<>();
   private static final ThreadLocal<XPath> xpathTL = new ThreadLocal<>();
 
+  static {
+    HttpClientUtil.HttpClientFactory.setHttpClientImpl(SolrHttpClient.SolrDefaultHttpClient.class, SolrHttpClient.SolrSystemDefaultHttpClient.class);
+  }
+
   public static DocumentBuilder getXmlDocumentBuilder() {
     try {
       DocumentBuilder builder = builderTL.get();