blob: 86ec41d21f03d1823a8c1e001149f96f1dee1328 [file] [log] [blame]
/*
* 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.core;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.solr.api.CallInfo;
import org.apache.solr.api.Command;
import org.apache.solr.api.EndPoint;
import org.apache.solr.api.V2HttpCall;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.common.util.Utils;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.schema.FieldType;
import org.apache.solr.security.PermissionNameProvider;
import org.apache.solr.util.plugin.PluginInfoInitialized;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.common.params.CommonParams.NAME;
import static org.apache.solr.common.params.CommonParams.PACKAGE;
import static org.apache.solr.common.params.CommonParams.VERSION;
import static org.apache.solr.core.PluginBag.closeQuietly;
@EndPoint(spec = "node.ext", permission = PermissionNameProvider.Name.CUSTOM_PERM)
public class ContainerRequestHandlers {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
final CoreContainer coreContainer;
private Map<String, Handler> customHandlers = new HashMap<>();
ContainerRequestHandlers(CoreContainer coreContainer) {
this.coreContainer = coreContainer;
}
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) {
int v = req.getParams().getInt(ConfigOverlay.ZNODEVER, -1);
if (v >= 0) {
log.debug("expected version : {} , my version {}", v, coreContainer.getPackageBag().myVersion);
ZkStateReader zkStateReader = coreContainer.getZkController().getZkStateReader();
try {
zkStateReader.forceRefreshClusterProps(v);
} catch (SolrException e) {
log.error("Error refreshing state ", e);
throw e;
}
}
rsp.add("metadata", (MapWriter) ew -> ew.put(VERSION,
coreContainer.getZkController().zkStateReader.getClusterPropsVersion()));
rsp.add(SolrRequestHandler.TYPE, customHandlers.values());
}
@Command
public void call(CallInfo info) {
String name = ((V2HttpCall) info.req.getHttpSolrCall()).getUrlParts().get("handlerName");
if (name == null) {
handleRequestBody(info.req, info.rsp);
return;
}
Handler wrapper = customHandlers.get(name);
if (wrapper == null) {
String err = StrUtils.formatString(" No such handler: {0}, available handlers : {1}", name, customHandlers.keySet());
log.error(err);
throw new SolrException(SolrException.ErrorCode.NOT_FOUND, err);
}
wrapper.handler.handleRequest(info.req, info.rsp);
}
void updateReqHandlers(Map<String, Object> properties) {
Map m = (Map) properties.getOrDefault(SolrRequestHandler.TYPE, Collections.emptyMap());
if (m.isEmpty() && customHandlers.isEmpty()) return;
if (customHandlers.size() == m.size() && customHandlers.keySet().containsAll(m.keySet())) return;
log.debug("RequestHandlers being reloaded : {}", m.keySet());
Map<String, Handler> newCustomHandlers = new HashMap<>();
List<Handler> toBeClosed = new ArrayList<>();
for (Object o : m.entrySet()) {
Object v = ((Map.Entry) o).getValue();
String name = (String) ((Map.Entry) o).getKey();
if (v instanceof Map) {
Map metaData = (Map) v;
Handler existing = customHandlers.get(name);
if (existing == null || !existing.meta.equals(metaData)) {
String klas = (String) metaData.get(FieldType.CLASS_NAME);
if (klas != null) {
newCustomHandlers.put(name, new Handler(metaData));
} else {
log.error("Invalid requestHandler {}", Utils.toJSONString(v));
}
if (existing != null) {
toBeClosed.add(existing);
}
} else {
newCustomHandlers.put(name, existing);
}
} else {
log.error("Invalid data for requestHandler : {} , {}", name, v);
}
}
log.debug("Registering request handlers {} ", newCustomHandlers.keySet());
Map<String, Handler> old = customHandlers;
for (Map.Entry<String, Handler> e : old.entrySet()) {
if (!newCustomHandlers.containsKey(e.getKey())) {
toBeClosed.add(e.getValue());
}
}
customHandlers = newCustomHandlers;
for (Handler wrapper : toBeClosed) {
closeQuietly(wrapper);
}
}
private SolrRequestHandler createHandler(Map metaData) {
String pkg = (String) metaData.get(PACKAGE);
SolrRequestHandler inst = coreContainer.getPackageBag().newInstance((String) metaData.get(FieldType.CLASS_NAME),
SolrRequestHandler.class, pkg);
if (inst instanceof PluginInfoInitialized) {
((PluginInfoInitialized) inst).init(new PluginInfo(SolrRequestHandler.TYPE, metaData));
}
return inst;
}
class Handler implements MapWriter, PackageListeners.Listener, AutoCloseable {
SolrRequestHandler handler;
final String pkg;
int zkversion;
PluginInfo meta;
PackageBag.PackageInfo packageInfo;
String name;
@Override
public void writeMap(EntryWriter ew) throws IOException {
ew.put(NAME, name);
ew.put(ConfigOverlay.ZNODEVER, zkversion);
meta.attributes.forEach(ew.getBiConsumer());
}
Handler(Map meta) {
this.meta = new PluginInfo(SolrRequestHandler.TYPE, meta);
pkg = (String) meta.get("package");
this.handler = createHandler(meta);
if (pkg != null) {
this.packageInfo = coreContainer.getPackageBag().getPackageInfo(pkg);
coreContainer.getPackageBag().listenerRegistry.addListener(this);
}
}
@Override
public String packageName() {
return pkg;
}
@Override
public PluginInfo pluginInfo() {
return meta;
}
@Override
public void changed(PackageBag.PackageInfo info) {
if (this.packageInfo.znodeVersion < info.znodeVersion) {
this.handler = createHandler(meta.attributes);
this.packageInfo = info;
}
}
@Override
public void close() throws Exception {
closeQuietly(handler);
}
@Override
public PackageBag.PackageInfo packageInfo() {
return packageInfo;
}
}
}