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 16:28:21 UTC
svn commit: r1693432 - in /lucene/dev/trunk/solr: ./
core/src/java/org/apache/solr/request/ core/src/java/org/apache/solr/search/
core/src/java/org/apache/solr/servlet/ core/src/java/org/apache/solr/util/
solrj/src/java/org/apache/solr/client/solrj/imp...
Author: noble
Date: Thu Jul 30 14:28:20 2015
New Revision: 1693432
URL: http://svn.apache.org/r1693432
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/trunk/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java (with props)
lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/SolrHttpClient.java (with props)
lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrHttpContext.java (with props)
Modified:
lucene/dev/trunk/solr/CHANGES.txt
lucene/dev/trunk/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientConfigurer.java
lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientUtil.java
lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java
lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java
lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java
lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java
lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java
Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1693432&r1=1693431&r2=1693432&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Thu Jul 30 14:28:20 2015
@@ -376,6 +376,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 Chattopadhyay, Gregory Chanan, noble, Anshum Gupta)
+
================== 5.2.1 ==================
Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this release
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java?rev=1693432&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/request/SolrQueryRequestContext.java Thu Jul 30 14:28:20 2015
@@ -0,0 +1,45 @@
+package org.apache.solr.request;
+
+
+import org.apache.solr.client.solrj.impl.SolrHttpContext;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+/**
+ * A HttpContext derivative to encapsulate a server-side SolrQueryRequest
+ * object.
+ */
+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+"]";
+ }
+}
+
+
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java?rev=1693432&r1=1693431&r2=1693432&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java Thu Jul 30 14:28:20 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/trunk/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java?rev=1693432&r1=1693431&r2=1693432&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java Thu Jul 30 14:28:20 2015
@@ -2203,6 +2203,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/trunk/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java?rev=1693432&r1=1693431&r2=1693432&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java Thu Jul 30 14:28:20 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,6 +112,7 @@ 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);
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/SolrHttpClient.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/SolrHttpClient.java?rev=1693432&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/SolrHttpClient.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/SolrHttpClient.java Thu Jul 30 14:28:20 2015
@@ -0,0 +1,97 @@
+package org.apache.solr.util;
+
+import java.io.IOException;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.protocol.HttpContext;
+import org.apache.solr.client.solrj.impl.SolrHttpContext;
+import org.apache.solr.request.SolrQueryRequestContext;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.request.SolrRequestInfo;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+public class SolrHttpClient {
+
+ private static HttpContext getHttpContext() {
+ SolrRequestInfo requestInfo = SolrRequestInfo.getRequestInfo();
+ SolrQueryRequest request = requestInfo == null ? null : requestInfo.getReq();
+ return request == null ? SolrHttpContext.EMPTY_CONTEXT: new SolrQueryRequestContext(request);
+ }
+
+ public static class SolrSystemDefaultHttpClient extends SystemDefaultHttpClient {
+
+ public SolrSystemDefaultHttpClient() {
+ super();
+ }
+
+ @Override
+ public CloseableHttpResponse execute(HttpUriRequest request)
+ throws IOException {
+ return super.execute(request, getHttpContext());
+ }
+
+ @Override
+ public CloseableHttpResponse execute(HttpHost target, HttpRequest request)
+ throws IOException, ClientProtocolException {
+ return super.execute(target, request, getHttpContext());
+ }
+
+ @Override
+ public <T> T execute(HttpUriRequest request,
+ ResponseHandler<? extends T> responseHandler) throws IOException,
+ ClientProtocolException {
+ return super.execute(request, responseHandler, getHttpContext());
+ }
+ }
+
+ public static class SolrDefaultHttpClient extends DefaultHttpClient {
+
+ public SolrDefaultHttpClient(ClientConnectionManager cm) {
+ super(cm);
+ }
+
+ @Override
+ public CloseableHttpResponse execute(HttpUriRequest request)
+ throws IOException {
+ return super.execute(request, getHttpContext());
+ }
+
+ @Override
+ public CloseableHttpResponse execute(HttpHost target, HttpRequest request)
+ throws IOException, ClientProtocolException {
+ return super.execute(target, request, getHttpContext());
+ }
+
+ @Override
+ public <T> T execute(HttpUriRequest request,
+ ResponseHandler<? extends T> responseHandler) throws IOException,
+ ClientProtocolException {
+ return super.execute(request, responseHandler, getHttpContext());
+ }
+ }
+}
+
Modified: lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientConfigurer.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientConfigurer.java?rev=1693432&r1=1693431&r2=1693432&view=diff
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientConfigurer.java (original)
+++ lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientConfigurer.java Thu Jul 30 14:28:20 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/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientUtil.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientUtil.java?rev=1693432&r1=1693431&r2=1693432&view=diff
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientUtil.java (original)
+++ lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientUtil.java Thu Jul 30 14:28:20 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;
+ }
+ }
+
}
Added: lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrHttpContext.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrHttpContext.java?rev=1693432&view=auto
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrHttpContext.java (added)
+++ lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrHttpContext.java Thu Jul 30 14:28:20 2015
@@ -0,0 +1,52 @@
+package org.apache.solr.client.solrj.impl;
+
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.solr.client.solrj.SolrRequest;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+/**
+ * A HttpContext derivative to encapsulate a client-side SolrRequest
+ * object.
+ */
+public class SolrHttpContext extends HttpClientContext {
+ final protected static String SOLR_CONTEXT_KEY = "solr.context";
+
+ private SolrRequest solrRequest;
+
+ public static HttpContext EMPTY_CONTEXT = new SolrHttpContext();
+
+ protected SolrHttpContext() {
+ setAttribute(SOLR_CONTEXT_KEY, this);
+ }
+
+ public SolrHttpContext(SolrRequest request) {
+ this.solrRequest = request;
+ setAttribute(SOLR_CONTEXT_KEY, this);
+ }
+
+ public SolrRequest getSolrRequest() {
+ return solrRequest;
+ }
+
+ @Override
+ public String toString() {
+ return "[SolrHttpContext contains: "+solrRequest+"]";
+ }
+}
Modified: lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java?rev=1693432&r1=1693431&r2=1693432&view=diff
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java (original)
+++ lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java Thu Jul 30 14:28:20 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) {
@@ -140,9 +181,21 @@ public class ExecutorUtil {
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<>(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();
@@ -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/trunk/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java?rev=1693432&r1=1693431&r2=1693432&view=diff
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java (original)
+++ lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java Thu Jul 30 14:28:20 2015
@@ -21,23 +21,39 @@ 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.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 +73,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 +105,7 @@ public class BasicHttpSolrClientTest ext
parameters = null;
errorCode = null;
queryString = null;
+ cookies = null;
}
public static Integer errorCode = null;
@@ -92,12 +113,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 +133,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 +158,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 +181,7 @@ public class BasicHttpSolrClientTest ext
setHeaders(req);
setParameters(req);
setQueryString(req);
+ setCookies(req);
if (null != errorCode) {
try {
resp.sendError(errorCode);
@@ -593,6 +634,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));
+ 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/trunk/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java?rev=1693432&r1=1693431&r2=1693432&view=diff
==============================================================================
--- lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java (original)
+++ lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java Thu Jul 30 14:28:20 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/trunk/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java?rev=1693432&r1=1693431&r2=1693432&view=diff
==============================================================================
--- lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java (original)
+++ lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java Thu Jul 30 14:28:20 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/trunk/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java?rev=1693432&r1=1693431&r2=1693432&view=diff
==============================================================================
--- lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java (original)
+++ lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java Thu Jul 30 14:28:20 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();