/*******************************************************************************
* Copyright (c) 2006-2012
* Software Technology Group, Dresden University of Technology
* DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Software Technology Group - TU Dresden, Germany;
* DevBoost GmbH - Berlin, Germany
* - initial API and implementation
******************************************************************************/
package org.reuseware.sokan.index.solr;
import static org.reuseware.sokan.index.solr.SolrConst.SYS_FIELD_ID;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.io.File;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.core.SolrConfig;
import org.apache.solr.core.SolrCore;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.schema.IndexSchema;
import org.reuseware.sokan.ID;
import org.reuseware.sokan.IndexRow;
import org.reuseware.sokan.index.SokanIndexPlugin;
import org.reuseware.sokan.index.util.CoreUtil;
import org.reuseware.sokan.index.util.ResourceUtil;
/**
* Singleton that controls the Solr index.
*/
public final class SolrControler {
/**
* The singleton instance.
*/
public static final SolrControler INSTANCE = new SolrControler();
private static final String OR = " OR ";
private static final int DEFAULT_ROWS = 100;
private String solrHome;
private String confDir;
private String solrData;
private SolrConfig solrConfig;
private IndexSchema indexSchema;
private SolrServer solrServer;
private CoreContainer coreContainer;
private SolrControler() {
solrHome = SolrUtil.getSolrHome();
confDir = SolrUtil.getConfigurationDir();
solrData = SolrUtil.getDataDir();
SolrUtil.configureLogger(Level.SEVERE);
setupSolrHome();
startSolrServer();
}
/**
* @return the Solr server
*/
public SolrServer getServer() {
return solrServer;
}
/**
* @return the indexSchema
*/
public IndexSchema getIndexSchema() {
return indexSchema;
}
/**
* Queries for all index rows.
*
* @return all index rows
*/
public List<IndexRow> query() {
return query("*:*", -1);
}
/**
* @return the names of all fields (columns) in the index
*/
public Set<String> getFieldNames() {
NamedList<Object> list = adminQuery();
if (list == null) {
return null;
}
Set<String> fields = new TreeSet<String>();
@SuppressWarnings("unchecked")
NamedList<Object> fieldStatistic = (NamedList<Object>) list.get("fields");
for (Entry<String, ?> entry : fieldStatistic) {
fields.add(entry.getKey());
}
return fields;
}
/**
* Queries for the index row of the artifact with the given ID.
*
* @param artifactID the artifact's ID
* @return the artifact's index row
*/
public IndexRow query(ID artifactID) {
String idString = ResourceUtil.idString(artifactID);
idString = SolrUtil.prepareForQuery(idString);
List<IndexRow> result = query(SYS_FIELD_ID + ":" + idString, 1);
if (result == null || result.size() < 1) {
return null;
}
return result.get(0);
}
/**
* Queries for all index rows of the artifacts with the given IDs.
*
* @param artifactIDs the artifacts' IDs
* @return the artifacts' index rows
*/
public List<IndexRow> query(Collection<ID> artifactIDs) {
String idString;
String queryString = "";
for (ID id : artifactIDs) {
idString = ResourceUtil.idString(id);
idString = SolrUtil.prepareForQuery(idString);
queryString += SYS_FIELD_ID + ":" + idString + OR;
}
if (queryString.length() != 0) {
queryString = CoreUtil.trimLastString(queryString, OR);
}
return query(queryString, artifactIDs.size());
}
/**
* Executes the query but limits the result to the given number of rows.
*
* @param queryString the query string
* @param resultRows maximum number of result rows
* @return the query result
*/
public List<IndexRow> query(String queryString, int resultRows) {
if (queryString == null || queryString.equals("")) {
return new ArrayList<IndexRow>();
}
SolrQuery query = new SolrQuery();
query.setQuery(queryString);
limit(query, resultRows);
QueryResponse rsp = query(query);
if (rsp == null) {
return null;
} else {
return SolrUtil.toIndexRows(rsp.getResults());
}
}
/**
* Executes the given Solr query.
*
* @param query the Solr query
* @return the query result
*/
public QueryResponse query(SolrQuery query) {
if (query == null) {
return null;
}
QueryResponse rsp = null;
query.add("debugQuery", "true");
try {
rsp = solrServer.query(query);
} catch (Exception e) {
SokanIndexPlugin.logError("", e);
}
if (rsp == null) {
return null;
}
return rsp;
}
/**
* Stops the Solr server.
*/
public void stopServer() {
if (coreContainer != null) {
coreContainer.shutdown();
}
}
private boolean isCorrectSchema() {
solrConfig = buildSolrConfig();
try {
indexSchema = buildIndexSchema(solrConfig);
} catch (RuntimeException e) {
// schema.xml doesn't exist
return false;
}
if (SolrUtil.wellFormed(indexSchema)) {
return true;
}
solrConfig = null;
indexSchema = null;
return false;
}
/**
* @return the size of the index.
*/
public int getSize() {
NamedList<Object> list = adminQuery();
if (list == null) {
return DEFAULT_ROWS;
}
@SuppressWarnings("unchecked")
NamedList<Object> indexStatistic = (NamedList<Object>) list.get("index");
return (Integer) indexStatistic.get("numDocs");
// SolrQuery query = new SolrQuery().setQuery("*.*").setRows(0);
// QueryResponse rsp = query(query);
// long numFound = rsp.getResults().getNumFound();
// return new Integer((int) numFound);
}
/**
* Limits the number of result rows in the given Solr query.
*
* @param query the Solr query
* @param resultRows maximum number of result rows
*/
public void limit(SolrQuery query, int resultRows) {
// Solr limits the number of result rows.
// see solrconfig.xml '<requestHandler name="standard" (...)'
if (resultRows < 0) {
query.setRows(getSize());
} else {
query.setRows(resultRows);
}
}
private NamedList<Object> adminQuery() {
SolrQuery query = new SolrQuery().setQuery("*.*").setQueryType(
"/admin/luke").setRows(0);
QueryResponse rsp = query(query);
return rsp.getResponse();
}
private boolean setupSolrHome() {
try {
// create solreHome and configDir if needed
File f = new File(confDir);
if (!f.exists()) {
f.mkdirs();
}
} catch (Exception e) {
SokanIndexPlugin.logError("", e);
return false;
}
// copy configuration file
if (!SolrUtil.copyIfNeeded(SolrConst.FILE_CONFIG, false)) {
return false;
}
// schema file
if (!SolrUtil.copyIfNeeded(SolrConst.FILE_SCHEMA, !isCorrectSchema())) {
return false;
}
// // copy configuration file
// if (!copyIfNeeded("protwords.txt"))
// return false;
//
// // copy configuration file
// if (!copyIfNeeded("stopwords.txt"))
// return false;
//
// // copy configuration file
// if (!copyIfNeeded("synonyms.txt"))
// return false;
return true;
}
private boolean startSolrServer() {
if (solrConfig == null) {
solrConfig = buildSolrConfig();
}
if (indexSchema == null) {
indexSchema = buildIndexSchema(solrConfig);
if (indexSchema == null) {
return false;
}
}
coreContainer = new CoreContainer(new SolrResourceLoader(
SolrResourceLoader.locateSolrHome()));
CoreDescriptor dcore = new CoreDescriptor(coreContainer, "", solrConfig
.getResourceLoader().getInstanceDir());
dcore.setConfigName(solrConfig.getResourceName());
dcore.setSchemaName(indexSchema.getResourceName());
SolrCore core = new SolrCore(SolrConst.CORE_NAME, solrData, solrConfig,
indexSchema, dcore);
coreContainer.register(core, false);
solrServer = new EmbeddedSolrServer(coreContainer, SolrConst.CORE_NAME);
return true;
}
private IndexSchema buildIndexSchema(SolrConfig solrConfig) {
if (solrConfig == null) {
return null;
}
IndexSchema indexSchema = new IndexSchema(solrConfig,
SolrConst.FILE_SCHEMA, null);
return indexSchema;
}
private SolrConfig buildSolrConfig() {
SolrConfig solrConfig = null;
try {
solrConfig = new SolrConfig(solrHome, SolrConst.FILE_CONFIG, null);
} catch (Exception e) {
SokanIndexPlugin.logError("", e);
}
return solrConfig;
}
}