/* * 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.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.solr.common.util.StrUtils; import org.apache.solr.request.SolrRequestHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** */ public final class RequestHandlers { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); protected final SolrCore core; final PluginBag<SolrRequestHandler> handlers; /** * Trim the trailing '/' if it's there, and convert null to empty string. * * we want: * /update/csv and * /update/csv/ * to map to the same handler * */ public static String normalize( String p ) { if(p == null) return ""; if( p.endsWith( "/" ) && p.length() > 1 ) return p.substring( 0, p.length()-1 ); return p; } public RequestHandlers(SolrCore core) { this.core = core; // we need a thread safe registry since methods like register are currently documented to be thread safe. handlers = new PluginBag<>(SolrRequestHandler.class, core, true); } /** * @return the RequestHandler registered at the given name */ public SolrRequestHandler get(String handlerName) { return handlers.get(normalize(handlerName)); } /** * Handlers must be initialized before calling this function. As soon as this is * called, the handler can immediately accept requests. * * This call is thread safe. * * @return the previous handler at the given path or null */ public SolrRequestHandler register( String handlerName, SolrRequestHandler handler ) { String norm = normalize(handlerName); if (handler == null) { return handlers.remove(norm); } return handlers.put(norm, handler); // return register(handlerName, new PluginRegistry.PluginHolder<>(null, handler)); } /** * Returns an unmodifiable Map containing the registered handlers */ public PluginBag<SolrRequestHandler> getRequestHandlers() { return handlers; } /** * Read solrconfig.xml and register the appropriate handlers * * This function should <b>only</b> be called from the SolrCore constructor. It is * not intended as a public API. * * While the normal runtime registration contract is that handlers MUST be initialized * before they are registered, this function does not do that exactly. * * This function registers all handlers first and then calls init() for each one. * * This is OK because this function is only called at startup and there is no chance that * a handler could be asked to handle a request before it is initialized. * * The advantage to this approach is that handlers can know what path they are registered * to and what other handlers are available at startup. * * Handlers will be registered and initialized in the order they appear in solrconfig.xml */ void initHandlersFromConfig(SolrConfig config) { List<PluginInfo> implicits = core.getImplicitHandlers(); // use link map so we iterate in the same order Map<String, PluginInfo> infoMap= new LinkedHashMap<>(); //deduping implicit and explicit requesthandlers for (PluginInfo info : implicits) infoMap.put(info.name,info); for (PluginInfo info : config.getPluginInfos(SolrRequestHandler.class.getName())) infoMap.put(info.name, info); ArrayList<PluginInfo> infos = new ArrayList<>(infoMap.values()); List<PluginInfo> modifiedInfos = new ArrayList<>(); for (PluginInfo info : infos) { modifiedInfos.add(applyInitParams(config, info)); } handlers.init(Collections.emptyMap(),core, modifiedInfos); handlers.alias(handlers.getDefault(), ""); log.debug("Registered paths: {}" , StrUtils.join(new ArrayList<>(handlers.keySet()) , ',' )); if (handlers.get("") == null && !handlers.alias("/select", "")) { if (handlers.get("") == null && !handlers.alias("standard", "")) { log.warn("no default request handler is registered (either '/select' or 'standard')"); } } } private PluginInfo applyInitParams(SolrConfig config, PluginInfo info) { List<InitParams> ags = new ArrayList<>(); String p = info.attributes.get(InitParams.TYPE); if(p!=null) { for (String arg : StrUtils.splitSmart(p, ',')) { if(config.getInitParams().containsKey(arg)) ags.add(config.getInitParams().get(arg)); else log.warn("INVALID paramSet {} in requestHandler {}", arg, info.toString()); } } for (InitParams args : config.getInitParams().values()) if(args.matchPath(info.name)) ags.add(args); if(!ags.isEmpty()){ info = info.copy(); for (InitParams initParam : ags) { initParam.apply(info); } } return info; } public void close() { handlers.close(); } }