/* Copyright (2005-2012) Schibsted ASA
* This file is part of Possom.
*
* Possom is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Possom is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Possom. If not, see <http://www.gnu.org/licenses/>.
*/
package no.sesat.search.query.token;
import javax.xml.parsers.ParserConfigurationException;
import no.sesat.commons.ioc.ContextWrapper;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import no.sesat.search.site.SiteKeyedFactoryInstantiationException;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.MDC;
/** Responsible for loading and serving the VeryFast (Fast Query Matching) Token Evaluator.
*
* One VeryFastTokenEvaluator is constructed per query and used for all tokens of type FAST.
*
* @version <tt>$Id$</tt>
*/
public final class FastQueryMatchingEvaluatorFactory extends AbstractEvaluatorFactory{
// Constants -----------------------------------------------------
private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool();
private static final Logger LOG = Logger.getLogger(FastQueryMatchingEvaluatorFactory.class);
private static final String ERR_FAILED_CONSTRUCTING_FAST_EVALUATOR = "Failed to construct the fast evaluator";
private static final String ERR_FAST_EVALUATOR_CREATOR_INTERRUPTED =
"Interrupted waiting for FastEvaluatorCreator. Evaluation on this query will fail.";
// Attributes -----------------------------------------------------
/** Because LIST_NAMES is encapsulated inside the VeryFastTokenEvaluator
* we keep a hint here in knowing if isReponsible() can simply and quickly always return false.
*/
private final boolean everApplicable;
private final Future fastEvaluatorCreator;
private VeryFastTokenEvaluator fastEvaluator;
// Constructors -----------------------------------------------------
public FastQueryMatchingEvaluatorFactory(final Context cxt) throws SiteKeyedFactoryInstantiationException {
super(cxt);
try {
everApplicable = VeryFastTokenEvaluator.initImpl(cxt);
}catch (ParserConfigurationException ex) {
throw new SiteKeyedFactoryInstantiationException(ERR_FAILED_CONSTRUCTING_FAST_EVALUATOR, ex);
}
fastEvaluatorCreator = EXECUTOR.submit(new FastEvaluatorCreator(cxt));
}
@Override
public TokenEvaluator getEvaluator(final TokenPredicate token) throws EvaluationException{
final Context cxt = getContext();
TokenEvaluator result = isResponsibleFor(token) ? getFastEvaluator() : null;
if(result == null && null != cxt.getSite().getParent()){
result = instanceOf(ContextWrapper.wrap(
Context.class,
cxt.getSite().getParent().getSiteContext(),
cxt
)).getEvaluator(token);
}
if(null == result || TokenEvaluationEngineImpl.ALWAYS_FALSE_EVALUATOR == result){
// if we cannot find an evaulator, then always fail evaluation.
// Rather than encourage a NullPointerException
result = TokenEvaluationEngineImpl.ALWAYS_FALSE_EVALUATOR;
}
return result;
}
@Override
public boolean isResponsibleFor(final TokenPredicate token) {
try {
return everApplicable && getFastEvaluator().isResponsibleFor(token);
}catch (EvaluationException ex) {
LOG.error("failed using VeryFastTokenEvaluator", ex);
return false;
}
}
// private -----------------------------------------------------
private VeryFastTokenEvaluator getFastEvaluator() throws EvaluationException {
try {
// when the root logger is set to DEBUG do not limit connection times
if(Logger.getRootLogger().getLevel().isGreaterOrEqual(Level.INFO)){
// default timeout is one second. TODO make configuration.
fastEvaluatorCreator.get(1000, TimeUnit.MILLISECONDS);
}else{
fastEvaluatorCreator.get();
}
} catch (InterruptedException ex) {
LOG.error(ERR_FAST_EVALUATOR_CREATOR_INTERRUPTED, ex);
throw new EvaluationException(ERR_FAILED_CONSTRUCTING_FAST_EVALUATOR, ex);
} catch (ExecutionException ex) {
LOG.error(ERR_FAST_EVALUATOR_CREATOR_INTERRUPTED, ex);
throw new EvaluationException(ERR_FAILED_CONSTRUCTING_FAST_EVALUATOR, ex);
} catch (TimeoutException ex) {
LOG.error(ERR_FAST_EVALUATOR_CREATOR_INTERRUPTED, ex);
throw new EvaluationException(ERR_FAILED_CONSTRUCTING_FAST_EVALUATOR, ex);
}
if( null == fastEvaluator ){
throw new EvaluationException(ERR_FAILED_CONSTRUCTING_FAST_EVALUATOR, new NullPointerException());
}
return fastEvaluator;
}
// inner classes -----------------------------------------------------
private final class FastEvaluatorCreator implements Runnable{
private final Context context;
private FastEvaluatorCreator(final Context cxt) {
this.context = cxt;
}
public void run() {
MDC.put("UNIQUE_ID", context.getUniqueId());
try {
fastEvaluator = new VeryFastTokenEvaluator(context);
} catch (EvaluationException ex) {
LOG.error(ERR_FAILED_CONSTRUCTING_FAST_EVALUATOR);
}
MDC.remove("UNIQUE_ID");
}
}
}