001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.solr.core;
017:
018: import java.net.URL;
019: import java.util.Collections;
020: import java.util.HashMap;
021: import java.util.LinkedHashMap;
022: import java.util.Map;
023: import java.util.logging.Logger;
024:
025: import javax.xml.xpath.XPathConstants;
026:
027: import org.apache.solr.request.SolrQueryRequest;
028: import org.apache.solr.request.SolrQueryResponse;
029: import org.apache.solr.request.SolrRequestHandler;
030: import org.apache.solr.request.StandardRequestHandler;
031: import org.apache.solr.util.DOMUtil;
032: import org.apache.solr.util.NamedList;
033: import org.apache.solr.util.SimpleOrderedMap;
034: import org.w3c.dom.Node;
035: import org.w3c.dom.NodeList;
036:
037: /**
038: * @author yonik
039: */
040: final class RequestHandlers {
041: public static Logger log = Logger.getLogger(RequestHandlers.class
042: .getName());
043:
044: public static final String DEFAULT_HANDLER_NAME = "standard";
045:
046: // Use a synchronized map - since the handlers can be changed at runtime,
047: // the map implementaion should be thread safe
048: private final Map<String, SolrRequestHandler> handlers = Collections
049: .synchronizedMap(new HashMap<String, SolrRequestHandler>());
050:
051: /**
052: * Trim the trailing '/' if its there.
053: *
054: * we want:
055: * /update/csv
056: * /update/csv/
057: * to map to the same handler
058: *
059: */
060: private static String normalize(String p) {
061: if (p != null && p.endsWith("/") && p.length() > 1)
062: return p.substring(0, p.length() - 1);
063:
064: return p;
065: }
066:
067: /**
068: * @return the RequestHandler registered at the given name
069: */
070: public SolrRequestHandler get(String handlerName) {
071: return handlers.get(normalize(handlerName));
072: }
073:
074: /**
075: * Handlers must be initalized before calling this function. As soon as this is
076: * called, the handler can immediatly accept requests.
077: *
078: * This call is thread safe.
079: *
080: * @return the previous handler at the given path or null
081: */
082: public SolrRequestHandler register(String handlerName,
083: SolrRequestHandler handler) {
084: String norm = normalize(handlerName);
085: if (handler == null) {
086: return handlers.remove(norm);
087: }
088: SolrRequestHandler old = handlers.put(norm, handler);
089: if (handlerName != null && handlerName != "") {
090: if (handler instanceof SolrInfoMBean) {
091: SolrInfoRegistry.getRegistry().put(handlerName,
092: (SolrInfoMBean) handler);
093: }
094: }
095: return old;
096: }
097:
098: /**
099: * Returns an unmodifieable Map containing the registered handlers
100: */
101: public Map<String, SolrRequestHandler> getRequestHandlers() {
102: return Collections.unmodifiableMap(handlers);
103: }
104:
105: /**
106: * Read solrconfig.xml and register the appropriate handlers
107: *
108: * This function should <b>only</b> be called from the SolrCore constructor. It is
109: * not intended as a public API.
110: *
111: * While the normal runtime registration contract is that handlers MUST be initalizad
112: * before they are registered, this function does not do that exactly.
113: *
114: * This funciton registers all handlers first and then calls init() for each one.
115: *
116: * This is OK because this function is only called at startup and there is no chance that
117: * a handler could be asked to handle a request before it is initalized.
118: *
119: * The advantage to this approach is that handlers can know what path they are registered
120: * to and what other handlers are avaliable at startup.
121: *
122: * Handlers will be registered and initalized in the order they appear in solrconfig.xml
123: */
124: @SuppressWarnings("unchecked")
125: void initHandlersFromConfig(Config config) {
126: NodeList nodes = (NodeList) config.evaluate("requestHandler",
127: XPathConstants.NODESET);
128:
129: if (nodes != null) {
130: // make sure it only once/handler and that that handlers get initalized in the
131: // order they were defined
132: Map<String, NamedList<Object>> names = new LinkedHashMap<String, NamedList<Object>>();
133: for (int i = 0; i < nodes.getLength(); i++) {
134: Node node = nodes.item(i);
135:
136: // In a production environment, we can tolerate an error in some request handlers,
137: // still load the others, and have a working system.
138: try {
139: String name = DOMUtil.getAttr(node, "name",
140: "requestHandler config");
141: String className = DOMUtil.getAttr(node, "class",
142: "requestHandler config");
143: String startup = DOMUtil.getAttr(node, "startup",
144: null);
145: NamedList<Object> args = DOMUtil
146: .childNodesToNamedList(node);
147:
148: // Perhaps lazy load the request handler with a wrapper
149: SolrRequestHandler handler = null;
150: if ("lazy".equals(startup)) {
151: log.info("adding lazy requestHandler: " + name
152: + "=" + className);
153: handler = new LazyRequestHandlerWrapper(
154: className, args);
155: } else {
156: Class<? extends SolrRequestHandler> clazz = Config
157: .findClass(className, new String[] {});
158: log.info("adding requestHandler: " + name + "="
159: + className);
160: handler = clazz.newInstance();
161: }
162:
163: SolrRequestHandler old = register(name, handler);
164: if (old != null) {
165: String msg = "multiple handlers registered on the same path! ignoring: "
166: + old;
167: Throwable t = new SolrException(
168: SolrException.ErrorCode.SERVER_ERROR,
169: msg);
170: SolrConfig.severeErrors.add(t);
171: SolrException.logOnce(log, null, t);
172: }
173: names.put(name, args);
174: } catch (Exception e) {
175: SolrConfig.severeErrors.add(e);
176: SolrException.logOnce(log, null, e);
177: }
178: }
179:
180: // Call init() on each handler after they have all been registered
181: for (Map.Entry<String, NamedList<Object>> reg : names
182: .entrySet()) {
183: try {
184: handlers.get(reg.getKey()).init(reg.getValue());
185: } catch (Exception e) {
186: SolrConfig.severeErrors.add(e);
187: SolrException.logOnce(log, null, e);
188: }
189: }
190: }
191:
192: //
193: // Get the default handler and add it in the map under null and empty
194: // to act as the default.
195: //
196: SolrRequestHandler handler = get(RequestHandlers.DEFAULT_HANDLER_NAME);
197: if (handler == null) {
198: handler = new StandardRequestHandler();
199: register(RequestHandlers.DEFAULT_HANDLER_NAME, handler);
200: }
201: register(null, handler);
202: register("", handler);
203: }
204:
205: /**
206: * The <code>LazyRequestHandlerWrapper</core> wraps any {@link SolrRequestHandler}.
207: * Rather then instanciate and initalize the handler on startup, this wrapper waits
208: * untill it is actually called. This should only be used for handlers that are
209: * unlikely to be used in the normal lifecycle.
210: *
211: * You can enable lazy loading in solrconfig.xml using:
212: *
213: * <pre>
214: * <requestHandler name="..." class="..." startup="lazy">
215: * ...
216: * </requestHandler>
217: * </pre>
218: *
219: * This is a private class - if there is a real need for it to be public, it could
220: * move
221: *
222: * @author ryan
223: * @version $Id: RequestHandlers.java 542679 2007-05-29 22:28:21Z ryan $
224: * @since solr 1.2
225: */
226: private static final class LazyRequestHandlerWrapper implements
227: SolrRequestHandler, SolrInfoMBean {
228: private String _className;
229: private NamedList _args;
230: private SolrRequestHandler _handler;
231:
232: public LazyRequestHandlerWrapper(String className,
233: NamedList args) {
234: _className = className;
235: _args = args;
236: _handler = null; // don't initalize
237: }
238:
239: /**
240: * In normal use, this function will not be called
241: */
242: public void init(NamedList args) {
243: // do nothing
244: }
245:
246: /**
247: * Wait for the first request before initalizing the wrapped handler
248: */
249: public void handleRequest(SolrQueryRequest req,
250: SolrQueryResponse rsp) {
251: getWrappedHandler().handleRequest(req, rsp);
252: }
253:
254: public synchronized SolrRequestHandler getWrappedHandler() {
255: if (_handler == null) {
256: try {
257: Class clazz = Config.findClass(_className,
258: new String[] {});
259: _handler = (SolrRequestHandler) clazz.newInstance();
260: _handler.init(_args);
261: } catch (Exception ex) {
262: throw new SolrException(
263: SolrException.ErrorCode.SERVER_ERROR,
264: "lazy loading error", ex);
265: }
266: }
267: return _handler;
268: }
269:
270: public String getHandlerClass() {
271: return _className;
272: }
273:
274: //////////////////////// SolrInfoMBeans methods //////////////////////
275:
276: public String getName() {
277: return "Lazy[" + _className + "]";
278: }
279:
280: public String getDescription() {
281: if (_handler == null) {
282: return getName();
283: }
284: return _handler.getDescription();
285: }
286:
287: public String getVersion() {
288: String rev = "$Revision: 542679 $";
289: if (_handler != null) {
290: rev += " :: " + _handler.getVersion();
291: }
292: return rev;
293: }
294:
295: public String getSourceId() {
296: String rev = "$Id: RequestHandlers.java 542679 2007-05-29 22:28:21Z ryan $";
297: if (_handler != null) {
298: rev += " :: " + _handler.getSourceId();
299: }
300: return rev;
301: }
302:
303: public String getSource() {
304: String rev = "$URL: https://svn.apache.org/repos/asf/lucene/solr/branches/branch-1.2/src/java/org/apache/solr/core/RequestHandlers.java $";
305: if (_handler != null) {
306: rev += "\n" + _handler.getSource();
307: }
308: return rev;
309: }
310:
311: public URL[] getDocs() {
312: if (_handler == null) {
313: return null;
314: }
315: return _handler.getDocs();
316: }
317:
318: public Category getCategory() {
319: return Category.QUERYHANDLER;
320: }
321:
322: public NamedList getStatistics() {
323: if (_handler != null) {
324: return _handler.getStatistics();
325: }
326: NamedList<String> lst = new SimpleOrderedMap<String>();
327: lst.add("note", "not initaized yet");
328: return lst;
329: }
330: }
331: }
|