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 org.w3c.dom.Document;
019: import org.w3c.dom.Node;
020: import org.xml.sax.SAXException;
021: import org.apache.solr.core.SolrCore;
022: import org.apache.solr.core.SolrException;
023: import org.apache.solr.util.DOMUtil;
024:
025: import javax.naming.Context;
026: import javax.naming.InitialContext;
027: import javax.naming.NamingException;
028: import javax.naming.NoInitialContextException;
029: import javax.xml.parsers.*;
030: import javax.xml.xpath.XPath;
031: import javax.xml.xpath.XPathFactory;
032: import javax.xml.xpath.XPathConstants;
033: import javax.xml.xpath.XPathExpressionException;
034: import javax.xml.namespace.QName;
035: import java.io.*;
036: import java.util.ArrayList;
037: import java.util.List;
038: import java.util.logging.Logger;
039: import java.net.URLClassLoader;
040: import java.net.URI;
041: import java.net.URL;
042: import java.net.MalformedURLException;
043:
044: /**
045: * @author yonik
046: * @version $Id: Config.java 542679 2007-05-29 22:28:21Z ryan $
047: */
048: public class Config {
049: public static final Logger log = Logger.getLogger(SolrCore.class
050: .getName());
051:
052: static final XPathFactory xpathFactory = XPathFactory.newInstance();
053:
054: private Document doc;
055: private String prefix;
056: private String name;
057:
058: public Config(String name, InputStream is, String prefix)
059: throws ParserConfigurationException, IOException,
060: SAXException {
061: this .name = name;
062: this .prefix = prefix;
063: if (prefix != null && !prefix.endsWith("/"))
064: prefix += '/';
065:
066: javax.xml.parsers.DocumentBuilder builder = DocumentBuilderFactory
067: .newInstance().newDocumentBuilder();
068: doc = builder.parse(is);
069:
070: try {
071: DOMUtil.substituteSystemProperties(doc);
072: } catch (SolrException e) {
073: SolrException.log(log, "Error in " + name, e);
074: throw e;
075: }
076: }
077:
078: public Document getDocument() {
079: return doc;
080: }
081:
082: public XPath getXPath() {
083: return xpathFactory.newXPath();
084: }
085:
086: private String normalize(String path) {
087: return (prefix == null || path.startsWith("/")) ? path : prefix
088: + path;
089: }
090:
091: public Object evaluate(String path, QName type) {
092: XPath xpath = xpathFactory.newXPath();
093: try {
094: String xstr = normalize(path);
095:
096: // TODO: instead of prepending /prefix/, we could do the search rooted at /prefix...
097: Object o = xpath.evaluate(xstr, doc, type);
098: return o;
099:
100: } catch (XPathExpressionException e) {
101: throw new SolrException(
102: SolrException.ErrorCode.SERVER_ERROR,
103: "Error in xpath:" + path + " for " + name, e, false);
104: }
105: }
106:
107: public Node getNode(String path, boolean errIfMissing) {
108: XPath xpath = xpathFactory.newXPath();
109: Node nd = null;
110: String xstr = normalize(path);
111:
112: try {
113: nd = (Node) xpath.evaluate(xstr, doc, XPathConstants.NODE);
114:
115: if (nd == null) {
116: if (errIfMissing) {
117: throw new RuntimeException(name + " missing "
118: + path);
119: } else {
120: log.fine(name + " missing optional " + path);
121: return null;
122: }
123: }
124:
125: log.finest(name + ":" + path + "=" + nd);
126: return nd;
127:
128: } catch (XPathExpressionException e) {
129: SolrException.log(log, "Error in xpath", e);
130: throw new SolrException(
131: SolrException.ErrorCode.SERVER_ERROR,
132: "Error in xpath:" + xstr + " for " + name, e, false);
133: } catch (SolrException e) {
134: throw (e);
135: } catch (Throwable e) {
136: SolrException.log(log, "Error in xpath", e);
137: throw new SolrException(
138: SolrException.ErrorCode.SERVER_ERROR,
139: "Error in xpath:" + xstr + " for " + name, e, false);
140: }
141: }
142:
143: public String getVal(String path, boolean errIfMissing) {
144: Node nd = getNode(path, errIfMissing);
145: if (nd == null)
146: return null;
147:
148: String txt = DOMUtil.getText(nd);
149:
150: log.fine(name + ' ' + path + '=' + txt);
151: return txt;
152:
153: /******
154: short typ = nd.getNodeType();
155: if (typ==Node.ATTRIBUTE_NODE || typ==Node.TEXT_NODE) {
156: return nd.getNodeValue();
157: }
158: return nd.getTextContent();
159: ******/
160: }
161:
162: public String get(String path) {
163: return getVal(path, true);
164: }
165:
166: public String get(String path, String def) {
167: String val = getVal(path, false);
168: return val != null ? val : def;
169: }
170:
171: public int getInt(String path) {
172: return Integer.parseInt(getVal(path, true));
173: }
174:
175: public int getInt(String path, int def) {
176: String val = getVal(path, false);
177: return val != null ? Integer.parseInt(val) : def;
178: }
179:
180: public boolean getBool(String path) {
181: return Boolean.parseBoolean(getVal(path, true));
182: }
183:
184: public boolean getBool(String path, boolean def) {
185: String val = getVal(path, false);
186: return val != null ? Boolean.parseBoolean(val) : def;
187: }
188:
189: public float getFloat(String path) {
190: return Float.parseFloat(getVal(path, true));
191: }
192:
193: public float getFloat(String path, float def) {
194: String val = getVal(path, false);
195: return val != null ? Float.parseFloat(val) : def;
196: }
197:
198: //
199: // classloader related functions
200: //
201:
202: private static final String project = "solr";
203: private static final String base = "org.apache" + "." + project;
204: private static final String[] packages = { "", "analysis.",
205: "schema.", "search.", "update.", "core.", "request.",
206: "handler.", "util." };
207:
208: public static Class findClass(String cname, String... subpackages) {
209: ClassLoader loader = getClassLoader();
210: if (subpackages.length == 0)
211: subpackages = packages;
212:
213: // first try cname == full name
214: try {
215: return Class.forName(cname, true, loader);
216: } catch (ClassNotFoundException e) {
217: String newName = cname;
218: if (newName.startsWith(project)) {
219: newName = cname.substring(project.length() + 1);
220: }
221: for (String subpackage : subpackages) {
222: try {
223: String name = base + '.' + subpackage + newName;
224: log.finest("Trying class name " + name);
225: return Class.forName(name, true, loader);
226: } catch (ClassNotFoundException e1) {
227: // ignore... assume first exception is best.
228: }
229: }
230:
231: throw new SolrException(
232: SolrException.ErrorCode.SERVER_ERROR,
233: "Error loading class '" + cname + "'", e, false);
234: }
235: }
236:
237: public static Object newInstance(String cname,
238: String... subpackages) {
239: Class clazz = findClass(cname, subpackages);
240: try {
241: return clazz.newInstance();
242: } catch (Exception e) {
243: throw new SolrException(
244: SolrException.ErrorCode.SERVER_ERROR,
245: "Error instantiating class " + clazz, e, false);
246: }
247: }
248:
249: private static String instanceDir; // solr home directory
250:
251: private static String normalizeDir(String path) {
252: if (path == null)
253: return null;
254: if (!(path.endsWith("/") || path.endsWith("\\"))) {
255: path += '/';
256: }
257: return path;
258: }
259:
260: public static void setInstanceDir(String dir) {
261: instanceDir = normalizeDir(dir);
262: classLoader = null;
263: log.info("Solr home set to '" + instanceDir + "'");
264: }
265:
266: public static String getInstanceDir() {
267: if (!isInstanceDirInitialized()) {
268: String home = null;
269: // Try JNDI
270: try {
271: Context c = new InitialContext();
272: home = (String) c.lookup("java:comp/env/solr/home");
273: log.info("Using JNDI solr.home: " + home);
274: } catch (NoInitialContextException e) {
275: log
276: .info("JNDI not configured for Solr (NoInitialContextEx)");
277: } catch (NamingException e) {
278: log.info("No /solr/home in JNDI");
279: } catch (RuntimeException ex) {
280: log
281: .warning("Odd RuntimeException while testing for JNDI: "
282: + ex.getMessage());
283: }
284:
285: // Now try system property
286: if (home == null) {
287: String prop = project + ".solr.home";
288: home = normalizeDir(System.getProperty(prop));
289: if (home != null) {
290: log
291: .info("using system property solr.home: "
292: + home);
293: }
294: }
295:
296: // if all else fails, try
297: if (home == null) {
298: home = project + '/';
299: log.info("Solr home defaulted to '" + instanceDir
300: + "' (could not find system property or JNDI)");
301: }
302:
303: setInstanceDir(home);
304: }
305: return instanceDir;
306: }
307:
308: public static boolean isInstanceDirInitialized() {
309: return instanceDir != null;
310: }
311:
312: // The directory where solr will look for config files by default.
313: // defaults to "./solr/conf/"
314: static String getConfigDir() {
315: return getInstanceDir() + "conf/";
316: }
317:
318: /** Singleton classloader loading resources specified in any configs */
319: private static ClassLoader classLoader = null;
320:
321: /**
322: * Returns the singleton classloader to be use when loading resources
323: * specified in any configs.
324: *
325: * <p>
326: * This loader will delegate to the context classloader when possible,
327: * otherwise it will attempt to resolve resources useing any jar files
328: * found in the "lib/" directory in the "Solr Home" directory.
329: * <p>
330: */
331: static ClassLoader getClassLoader() {
332: if (null == classLoader) {
333: classLoader = Thread.currentThread()
334: .getContextClassLoader();
335:
336: File f = new File(getInstanceDir() + "lib/");
337: if (f.canRead() && f.isDirectory()) {
338: File[] jarFiles = f.listFiles();
339: URL[] jars = new URL[jarFiles.length];
340: try {
341: for (int j = 0; j < jarFiles.length; j++) {
342: jars[j] = jarFiles[j].toURI().toURL();
343: log.info("Adding '" + jars[j].toString()
344: + "' to Solr classloader");
345: }
346: classLoader = URLClassLoader.newInstance(jars,
347: classLoader);
348: } catch (MalformedURLException e) {
349: SolrException.log(log,
350: "Can't construct solr lib class loader", e);
351: }
352: }
353: }
354: return classLoader;
355: }
356:
357: public static InputStream openResource(String resource) {
358: InputStream is = null;
359:
360: try {
361: File f = new File(resource);
362: if (!f.isAbsolute()) {
363: // try $CWD/solrconf/
364: f = new File(getConfigDir() + resource);
365: }
366: if (f.isFile() && f.canRead()) {
367: return new FileInputStream(f);
368: } else {
369: // try $CWD
370: f = new File(resource);
371: if (f.isFile() && f.canRead()) {
372: return new FileInputStream(f);
373: }
374: }
375:
376: ClassLoader loader = getClassLoader();
377: is = loader.getResourceAsStream(resource);
378: } catch (Exception e) {
379: throw new RuntimeException("Error opening " + resource, e);
380: }
381: if (is == null) {
382: throw new RuntimeException("Can't find resource '"
383: + resource + "' in classpath or '" + getConfigDir()
384: + "', cwd=" + System.getProperty("user.dir"));
385: }
386: return is;
387: }
388:
389: /**
390: * Accesses a resource by name and returns the (non comment) lines
391: * containing data.
392: *
393: * <p>
394: * A comment line is any line that starts with the character "#"
395: * </p>
396: *
397: * @param resource
398: * @return a list of non-blank non-comment lines with whitespace trimmed
399: * from front and back.
400: * @throws IOException
401: */
402: public static List<String> getLines(String resource)
403: throws IOException {
404: BufferedReader input = null;
405: try {
406: // todo - allow configurable charset?
407: input = new BufferedReader(new InputStreamReader(
408: openResource(resource), "UTF-8"));
409: } catch (UnsupportedEncodingException e) {
410: throw new RuntimeException(e);
411: }
412: ArrayList<String> lines = new ArrayList<String>();
413: for (String word = null; (word = input.readLine()) != null;) {
414: // skip comments
415: if (word.startsWith("#"))
416: continue;
417: word = word.trim();
418: // skip blank lines
419: if (word.length() == 0)
420: continue;
421: lines.add(word);
422: }
423: return lines;
424: }
425:
426: }
|