org.apache.solr.handler.admin.CoreAdminHandler Maven / Gradle / Ivy
Show all versions of solr-core Show documentation
/*
* 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.
*/
package org.apache.solr.handler.admin;
import java.io.File;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.solr.api.Api;
import org.apache.solr.cloud.CloudDescriptor;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CommonAdminParams;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.ExecutorUtil;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.logging.MDCLoggingContext;
import org.apache.solr.metrics.SolrMetricManager;
import org.apache.solr.metrics.SolrMetricsContext;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.security.PermissionNameProvider;
import org.apache.solr.common.util.SolrNamedThreadFactory;
import org.apache.solr.util.stats.MetricUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import static org.apache.solr.common.params.CoreAdminParams.ACTION;
import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.STATUS;
import static org.apache.solr.security.PermissionNameProvider.Name.CORE_EDIT_PERM;
import static org.apache.solr.security.PermissionNameProvider.Name.CORE_READ_PERM;
/**
*
* @since solr 1.3
*/
public class CoreAdminHandler extends RequestHandlerBase implements PermissionNameProvider {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected final CoreContainer coreContainer;
protected final Map> requestStatusMap;
private final CoreAdminHandlerApi coreAdminHandlerApi;
protected ExecutorService parallelExecutor = ExecutorUtil.newMDCAwareFixedThreadPool(50,
new SolrNamedThreadFactory("parallelCoreAdminExecutor"));
protected static int MAX_TRACKED_REQUESTS = 100;
public static String RUNNING = "running";
public static String COMPLETED = "completed";
public static String FAILED = "failed";
public static String RESPONSE = "Response";
public static String RESPONSE_STATUS = "STATUS";
public static String RESPONSE_MESSAGE = "msg";
public CoreAdminHandler() {
super();
// Unlike most request handlers, CoreContainer initialization
// should happen in the constructor...
this.coreContainer = null;
HashMap> map = new HashMap<>(3, 1.0f);
map.put(RUNNING, Collections.synchronizedMap(new LinkedHashMap()));
map.put(COMPLETED, Collections.synchronizedMap(new LinkedHashMap()));
map.put(FAILED, Collections.synchronizedMap(new LinkedHashMap()));
requestStatusMap = Collections.unmodifiableMap(map);
coreAdminHandlerApi = new CoreAdminHandlerApi(this);
}
/**
* Overloaded ctor to inject CoreContainer into the handler.
*
* @param coreContainer Core Container of the solr webapp installed.
*/
public CoreAdminHandler(final CoreContainer coreContainer) {
this.coreContainer = coreContainer;
HashMap> map = new HashMap<>(3, 1.0f);
map.put(RUNNING, Collections.synchronizedMap(new LinkedHashMap()));
map.put(COMPLETED, Collections.synchronizedMap(new LinkedHashMap()));
map.put(FAILED, Collections.synchronizedMap(new LinkedHashMap()));
requestStatusMap = Collections.unmodifiableMap(map);
coreAdminHandlerApi = new CoreAdminHandlerApi(this);
}
@Override
final public void init(@SuppressWarnings({"rawtypes"})NamedList args) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
"CoreAdminHandler should not be configured in solrconf.xml\n" +
"it is a special Handler configured directly by the RequestDispatcher");
}
@Override
public void initializeMetrics(SolrMetricsContext parentContext, String scope) {
super.initializeMetrics(parentContext, scope);
parallelExecutor = MetricUtils.instrumentedExecutorService(parallelExecutor, this, solrMetricsContext.getMetricRegistry(),
SolrMetricManager.mkName("parallelCoreAdminExecutor", getCategory().name(), scope, "threadPool"));
}
@Override
public Boolean registerV2() {
return Boolean.TRUE;
}
/**
* The instance of CoreContainer this handler handles. This should be the CoreContainer instance that created this
* handler.
*
* @return a CoreContainer instance
*/
public CoreContainer getCoreContainer() {
return this.coreContainer;
}
@Override
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
// Make sure the cores is enabled
try {
CoreContainer cores = getCoreContainer();
if (cores == null) {
throw new SolrException(ErrorCode.BAD_REQUEST,
"Core container instance missing");
}
//boolean doPersist = false;
final String taskId = req.getParams().get(CommonAdminParams.ASYNC);
final TaskObject taskObject = new TaskObject(taskId);
if(taskId != null) {
// Put the tasks into the maps for tracking
if (getRequestStatusMap(RUNNING).containsKey(taskId) || getRequestStatusMap(COMPLETED).containsKey(taskId) || getRequestStatusMap(FAILED).containsKey(taskId)) {
throw new SolrException(ErrorCode.BAD_REQUEST,
"Duplicate request with the same requestid found.");
}
addTask(RUNNING, taskObject);
}
// Pick the action
CoreAdminOperation op = opMap.get(req.getParams().get(ACTION, STATUS.toString()).toLowerCase(Locale.ROOT));
if (op == null) {
handleCustomAction(req, rsp);
return;
}
final CallInfo callInfo = new CallInfo(this, req, rsp, op);
String coreName = req.getParams().get(CoreAdminParams.CORE);
if (coreName == null) {
coreName = req.getParams().get(CoreAdminParams.NAME);
}
MDCLoggingContext.setCoreName(coreName);
if (taskId == null) {
callInfo.call();
} else {
try {
MDC.put("CoreAdminHandler.asyncId", taskId);
MDC.put("CoreAdminHandler.action", op.action.toString());
parallelExecutor.execute(() -> {
boolean exceptionCaught = false;
try {
callInfo.call();
taskObject.setRspObject(callInfo.rsp);
} catch (Exception e) {
exceptionCaught = true;
taskObject.setRspObjectFromException(e);
} finally {
removeTask("running", taskObject.taskId);
if (exceptionCaught) {
addTask("failed", taskObject, true);
} else {
addTask("completed", taskObject, true);
}
}
});
} finally {
MDC.remove("CoreAdminHandler.asyncId");
MDC.remove("CoreAdminHandler.action");
}
}
} finally {
rsp.setHttpCaching(false);
}
}
/**
* Handle Custom Action.
*
* This method could be overridden by derived classes to handle custom actions.
By default - this method throws a
* solr exception. Derived classes are free to write their derivation if necessary.
*/
protected void handleCustomAction(SolrQueryRequest req, SolrQueryResponse rsp) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unsupported operation: " +
req.getParams().get(ACTION));
}
public static ImmutableMap paramToProp = ImmutableMap.builder()
.put(CoreAdminParams.CONFIG, CoreDescriptor.CORE_CONFIG)
.put(CoreAdminParams.SCHEMA, CoreDescriptor.CORE_SCHEMA)
.put(CoreAdminParams.DATA_DIR, CoreDescriptor.CORE_DATADIR)
.put(CoreAdminParams.ULOG_DIR, CoreDescriptor.CORE_ULOGDIR)
.put(CoreAdminParams.CONFIGSET, CoreDescriptor.CORE_CONFIGSET)
.put(CoreAdminParams.LOAD_ON_STARTUP, CoreDescriptor.CORE_LOADONSTARTUP)
.put(CoreAdminParams.TRANSIENT, CoreDescriptor.CORE_TRANSIENT)
.put(CoreAdminParams.SHARD, CoreDescriptor.CORE_SHARD)
.put(CoreAdminParams.COLLECTION, CoreDescriptor.CORE_COLLECTION)
.put(CoreAdminParams.ROLES, CoreDescriptor.CORE_ROLES)
.put(CoreAdminParams.CORE_NODE_NAME, CoreDescriptor.CORE_NODE_NAME)
.put(ZkStateReader.NUM_SHARDS_PROP, CloudDescriptor.NUM_SHARDS)
.put(CoreAdminParams.REPLICA_TYPE, CloudDescriptor.REPLICA_TYPE)
.build();
protected static Map buildCoreParams(SolrParams params) {
Map coreParams = new HashMap<>();
// standard core create parameters
for (Map.Entry entry : paramToProp.entrySet()) {
String value = params.get(entry.getKey(), null);
if (StringUtils.isNotEmpty(value)) {
coreParams.put(entry.getValue(), value);
}
}
// extra properties
Iterator paramsIt = params.getParameterNamesIterator();
while (paramsIt.hasNext()) {
String param = paramsIt.next();
if (param.startsWith(CoreAdminParams.PROPERTY_PREFIX)) {
String propName = param.substring(CoreAdminParams.PROPERTY_PREFIX.length());
String propValue = params.get(param);
coreParams.put(propName, propValue);
}
if (param.startsWith(ZkController.COLLECTION_PARAM_PREFIX)) {
coreParams.put(param, params.get(param));
}
}
return coreParams;
}
protected static String normalizePath(String path) {
if (path == null)
return null;
path = path.replace('/', File.separatorChar);
path = path.replace('\\', File.separatorChar);
return path;
}
public static ModifiableSolrParams params(String... params) {
ModifiableSolrParams msp = new ModifiableSolrParams();
for (int i=0; i getRequestStatusMap(String key) {
return requestStatusMap.get(key);
}
/**
* Method to ensure shutting down of the ThreadPool Executor.
*/
public void shutdown() {
if (parallelExecutor != null)
ExecutorUtil.shutdownAndAwaitTermination(parallelExecutor);
}
private static final Map opMap = new HashMap<>();
static class CallInfo {
final CoreAdminHandler handler;
final SolrQueryRequest req;
final SolrQueryResponse rsp;
final CoreAdminOperation op;
CallInfo(CoreAdminHandler handler, SolrQueryRequest req, SolrQueryResponse rsp, CoreAdminOperation op) {
this.handler = handler;
this.req = req;
this.rsp = rsp;
this.op = op;
}
void call() throws Exception {
op.execute(this);
}
}
@Override
public Collection getApis() {
return coreAdminHandlerApi.getApis();
}
static {
for (CoreAdminOperation op : CoreAdminOperation.values())
opMap.put(op.action.toString().toLowerCase(Locale.ROOT), op);
}
/**
* used by the INVOKE action of core admin handler
*/
public interface Invocable {
Map invoke(SolrQueryRequest req);
}
interface CoreAdminOp {
/**
* @param it request/response object
*
* If the request is invalid throw a SolrException with SolrException.ErrorCode.BAD_REQUEST ( 400 )
* If the execution of the command fails throw a SolrException with SolrException.ErrorCode.SERVER_ERROR ( 500 )
*
* Any non-SolrException's are wrapped at a higher level as a SolrException with SolrException.ErrorCode.SERVER_ERROR.
*/
void execute(CallInfo it) throws Exception;
}
}