/** * Copyright 2008 The University of North Carolina at Chapel Hill * * Licensed 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 edu.unc.lib.dl.search.solr.util; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.solr.client.solrj.SolrServer; import org.apache.solr.client.solrj.impl.HttpSolrServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility class which stores Solr index addressing and instantiation settings from a properties file. * * @author bbpennel */ public class SolrSettings extends AbstractSettings { private final Logger LOG = LoggerFactory.getLogger(SolrSettings.class); private String path; private String url; private String core; private int socketTimeout; private int connectionTimeout; private int defaultMaxConnectionsPerHost; private int maxConnections; private int maxRetries; // Mapping of field keys to internal solr field names private HashMap<String, String> fieldNames; // Reverse of fieldName, for translating from the internal solr field name to the general field identification key private HashMap<String, String> fieldNameToKey; public SolrSettings() { fieldNames = new HashMap<String, String>(); } /** * Initialize SolrSettings attributes from a properties input object * * @param properties * solr settings properties object. */ public void setProperties(Properties properties) { LOG.debug("Setting properties."); this.setPath(properties.getProperty("solr.path", "")); this.setCore(properties.getProperty("solr.core", "")); this.setSocketTimeout(Integer.parseInt(properties.getProperty("solr.socketTimeout", "1000"))); this.setConnectionTimeout(Integer.parseInt(properties.getProperty("solr.connectionTimeout", "100"))); this.setDefaultMaxConnectionsPerHost(Integer.parseInt(properties.getProperty("solr.defaultMaxConnectionsPerHost", "100"))); this.setMaxConnections(Integer.parseInt(properties.getProperty("solr.maxConnections", "100"))); this.setMaxRetries(Integer.parseInt(properties.getProperty("solr.maxRetries", "1"))); // Store the URL to the Solr index for non-embedded connections. Add the core if specified. if (this.path != null) { this.url = this.path; if (this.core != null && !this.core.equals("")) { if (this.url.lastIndexOf("/") != this.url.length() - 1) this.url += "/"; this.url += this.core; } } populateMapFromProperty("solr.field.", fieldNames, properties); fieldNameToKey = getInvertedHashMap(fieldNames); LOG.debug(this.toStringStatic()); } /** * Retrieve a SolrServer object according to the configuration specified in settings. */ public SolrServer getSolrServer() { SolrServer server = null; try { LOG.debug("Establishing Solr server:" + getUrl()); server = new HttpSolrServer(getUrl()); ((HttpSolrServer) server).setSoTimeout(getSocketTimeout()); // socket read timeout ((HttpSolrServer) server).setConnectionTimeout(getConnectionTimeout()); ((HttpSolrServer) server).setDefaultMaxConnectionsPerHost(getDefaultMaxConnectionsPerHost()); ((HttpSolrServer) server).setMaxTotalConnections(getMaxConnections()); ((HttpSolrServer) server).setMaxRetries(maxRetries); } catch (Exception e) { LOG.error("Error initializing Solr Server instance", e); } return server; } private static Pattern escapeReservedWords = Pattern.compile("\\b(?<!\\*)(AND|OR|NOT)\\b(?!\\*)"); public static String sanitize(String value) { if (value == null) return value; return escapeReservedWords.matcher(escapeQueryChars(value)).replaceAll("'$1'"); } public static String escapeQueryChars(String s) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); // These characters are part of the query syntax and must be escaped if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':' || c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~' || c == '?' || c == '|' || c == '&' || c == ';' || c == '/' || Character.isWhitespace(c)) { sb.append('\\'); } sb.append(c); } return sb.toString(); } private static Pattern splitTermFragmentsRegex = Pattern.compile("(\"(([^\"]|\\\")*)\"|([^\" ,]+))"); /** * Retrieves all the search term fragments contained in the selected field. Fragments are either single words * separated by non-alphanumeric characters, or phrases encapsulated by quotes. * * @param value * @return */ public static List<String> getSearchTermFragments(String value) { if (value == null) return null; Matcher matcher = splitTermFragmentsRegex.matcher(value); List<String> fragments = new ArrayList<String>(); while (matcher.find()) { if (matcher.groupCount() == 4) { boolean quoted = matcher.group(2) != null; String fragment = quoted ? matcher.group(2) : matcher.group(4); fragment = sanitize(fragment.replace("\\\"", "\"")); if (quoted || fragment.indexOf('\\') > -1) fragment = '"' + fragment + '"'; fragments.add(fragment); } } return fragments; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getCore() { return core; } public void setCore(String core) { this.core = core; } public int getSocketTimeout() { return socketTimeout; } public void setSocketTimeout(int socketTimeout) { this.socketTimeout = socketTimeout; } public int getConnectionTimeout() { return connectionTimeout; } public void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; } public int getDefaultMaxConnectionsPerHost() { return defaultMaxConnectionsPerHost; } public void setDefaultMaxConnectionsPerHost(int defaultMaxConnectionsPerHost) { this.defaultMaxConnectionsPerHost = defaultMaxConnectionsPerHost; } public int getMaxConnections() { return maxConnections; } public void setMaxConnections(int maxConnections) { this.maxConnections = maxConnections; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String toStringStatic() { String output = " path: " + path; output += "\n url: " + url; output += "\n core: " + core; output += "\n socketTimeout: " + socketTimeout; output += "\n connectionTimeout: " + connectionTimeout; output += "\n defaultMaxConnectionsPerHost: " + defaultMaxConnectionsPerHost; output += "\n maxConnections: " + maxConnections; output += "\n fieldNames: " + fieldNames; return output; } /** * Returns the field identification key for the internal solr field name given * * @param name * @return */ public String getFieldKey(String name) { return fieldNameToKey.get(name); } public HashMap<String, String> getFieldNameToKey() { return this.fieldNameToKey; } /** * Returns the internal solr field name for the field identified by key * * @param key * @return */ public String getFieldName(String key) { return fieldNames.get(key); } public HashMap<String, String> getFieldNames() { return fieldNames; } public void setFieldNames(HashMap<String, String> fieldNames) { this.fieldNames = fieldNames; fieldNameToKey = getInvertedHashMap(fieldNames); } public int getMaxRetries() { return maxRetries; } public void setMaxRetries(int maxRetries) { this.maxRetries = maxRetries; } }