/**
* Copyright (c) 2008--2014 Red Hat, Inc.
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/
package com.redhat.satellite.search.rpc.handlers;
import com.redhat.satellite.search.db.DatabaseManager;
import com.redhat.satellite.search.db.Query;
import com.redhat.satellite.search.index.IndexManager;
import com.redhat.satellite.search.index.IndexingException;
import com.redhat.satellite.search.index.Result;
import com.redhat.satellite.search.index.QueryParseException;
import com.redhat.satellite.search.scheduler.ScheduleManager;
import org.apache.log4j.Logger;
import org.apache.lucene.search.BooleanQuery;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import redstone.xmlrpc.XmlRpcFault;
/**
* XML-RPC handler which handles calls for indexing
*
* @version $Rev$
*/
public class IndexHandler {
private static Logger log = Logger.getLogger(IndexHandler.class);
private IndexManager indexManager;
private DatabaseManager databaseManager;
public static final int QUERY_ERROR = 100;
public static final int INDEX_ERROR = 200;
public static final int DB_ERROR = 300;
public static final String DEFAULT_LANG = new Locale("EN", "US").toString();
/**
* Constructor
*
* @param idxManager
* Search engine interface
*/
public IndexHandler(IndexManager idxManager, DatabaseManager dbMgr,
ScheduleManager schedMgr) {
indexManager = idxManager;
databaseManager = dbMgr;
}
/**
* Search index -
* assumes English language as default language
*
* @param sessionId
* user's application session id
* @param indexName
* index to use
* @param query
* search query
* @return list of document ids as results
* @throws XmlRpcFault something bad happened
*/
public List<Result> search(long sessionId, String indexName, String query)
throws XmlRpcFault {
return search(sessionId, indexName, query, DEFAULT_LANG);
}
/**
* Search index -
* assumes English language as default language
*
* @param sessionId
* user's application session id
* @param indexName
* index to use
* @param query
* search query
* @param isFineGrained
* if set will restrict matches to be stricter and less forgiving
* @return list of document ids as results
* @throws XmlRpcFault something bad happened
*/
public List<Result> search(long sessionId, String indexName, String query,
boolean isFineGrained)
throws XmlRpcFault {
return search(sessionId, indexName, query, DEFAULT_LANG, isFineGrained);
}
/**
* Search index
*
* @param sessionId
* user's application session id
* @param indexName
* index to use
* @param query
* search query
* @param lang
* language
* @return list of document ids as results
* @throws XmlRpcFault something bad happened
*/
public List<Result> search(long sessionId, String indexName, String query,
String lang) throws XmlRpcFault {
return search(sessionId, indexName, query, lang, false);
}
/**
* Search index
*
* @param sessionId
* user's application session id
* @param indexName
* index to use
* @param query
* search query
* @param lang
* language
* @param isFineGrained
* if set will restrict matches to be stricter and less forgiving
* @return list of document ids as results
* @throws XmlRpcFault something bad happened
*/
public List<Result> search(long sessionId, String indexName, String query,
String lang, boolean isFineGrained)
throws XmlRpcFault {
if (log.isDebugEnabled()) {
log.debug("IndexHandler:: searching for: " + query + ", indexName = " +
indexName + ", lang = " + lang);
}
boolean retry = true;
while (retry) {
try {
retry = false;
List<Result> hits = indexManager.search(indexName, query, lang,
isFineGrained);
if (indexName.equals("package") || indexName.equals("errata")
|| indexName.equals("server")) {
return screenHits(sessionId, indexName, hits);
}
return hits;
}
catch (IndexingException e) {
log.error("Caught exception: ", e);
throw new XmlRpcFault(INDEX_ERROR, e.getMessage());
}
catch (QueryParseException e) {
log.error("Caught exception: ", e);
throw new XmlRpcFault(QUERY_ERROR, e.getMessage());
}
catch (SQLException e) {
log.error("Caught exception: ", e);
throw new XmlRpcFault(DB_ERROR, e.getMessage());
}
catch (BooleanQuery.TooManyClauses e) {
int oldQueries = BooleanQuery.getMaxClauseCount();
if (Integer.MAX_VALUE / 2 > oldQueries) {
// increase number of max clause count
// if there's no overflow danger
int newQueries = oldQueries * 2;
log.error("Too many hits for query: " + oldQueries +
". Increasing max clause count to " + newQueries +
"\nexception message: " + e.getMessage());
BooleanQuery.setMaxClauseCount(newQueries);
retry = true;
}
else {
// there's no more help
throw e;
}
}
}
// return just because of compiler
return null;
}
private List<Result> screenHits(long sessionId, String indexName,
List<Result> hits) throws SQLException {
if (hits == null || hits.size() == 0) {
if (log.isDebugEnabled()) {
log.debug("NO HITS FOUND");
}
return Collections.<Result>emptyList();
}
List<Long> ids = new ArrayList<Long>();
for (Result pr : hits) {
ids.add(new Long(pr.getId()));
}
Query<String> query = null;
if ("package".equals(indexName)) {
query = databaseManager.getQuery("verifyPackageVisibility");
}
else if ("errata".equals(indexName)) {
query = databaseManager.getQuery("verifyErrataVisibility");
}
else if ("server".equals(indexName)) {
query = databaseManager.getQuery("verifyServerVisibility");
}
else {
if (log.isDebugEnabled()) {
log.debug("screenHits(" + indexName +
") no 'screening of results' performed");
log.debug("results: " + hits);
}
return hits;
}
try {
Set<String> visible = new HashSet<String>();
// we have to batch the visibility query in clause to no more
// than 1000 for oracle
final int batch_size = 1000;
for (int i = 0; i < ids.size(); i += batch_size) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("session_id", sessionId);
// sub list includes the first index and excludes the last.
// so the proper last index of ids to pass to subList is ids.size()
params.put("id_list", ids.subList(i, (i + batch_size) <= ids.size() ? i +
batch_size : ids.size()));
visible.addAll(query.loadList(params));
}
if (log.isDebugEnabled()) {
log.debug("results: " + visible);
}
// add the PackageResults that match the visible list
List<Result> realResults = new ArrayList<Result>();
for (Result pr : hits) {
if (visible.contains(pr.getId())) {
realResults.add(pr);
}
}
return realResults;
}
finally {
query.close();
}
}
}