/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.backend.impl.lucene.analysis;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.DelegatingAnalyzerWrapper;
import org.hibernate.search.analyzer.impl.ScopedLuceneAnalyzer;
import org.hibernate.search.exception.AssertionFailure;
/**
* An Analyzer implementation which can dynamically be reconfigured. We use this as temporary solution to workaround
* LUCENE-6212, until a better solution is found.
*
* @author Sanne Grinovero (C) 2015 Red Hat Inc.
*/
public final class ConcurrentlyMutableAnalyzer extends DelegatingAnalyzerWrapper {
private final AtomicReference<ScopedLuceneAnalyzer> current = new AtomicReference<>();
public ConcurrentlyMutableAnalyzer(Analyzer initialAnalyzer) {
super( new ResettableReuseStrategy() );
if ( initialAnalyzer instanceof ScopedLuceneAnalyzer ) {
current.set( (ScopedLuceneAnalyzer) initialAnalyzer );
}
else {
current.set( new ScopedLuceneAnalyzer( initialAnalyzer ) );
}
}
@Override
protected Analyzer getWrappedAnalyzer(String fieldName) {
return current.get();
}
/**
* Checks if the currently set Analyzer is the same as the
* proposed one, in which case there is no need for
* replacements or locking.
* Correct concurrency control requires external locking!
* @param analyzer the {@link ScopedLuceneAnalyzer} to use for comparison
* @return true if there is no need to replace the current Analyzer
*/
public boolean isCompatibleWith(ScopedLuceneAnalyzer analyzer) {
ScopedLuceneAnalyzer currentAnalyzer = current.get();
return currentAnalyzer.isCompositeOfSameInstances( analyzer );
}
/**
* Correct concurrency control requires external locking!
* @param analyzer the {@link ScopedLuceneAnalyzer} to use for locking
*/
public void updateAnalyzer(ScopedLuceneAnalyzer analyzer) {
current.set( analyzer );
}
private static final class ResettableReuseStrategy extends ReuseStrategy {
@Override
public TokenStreamComponents getReusableComponents(Analyzer analyzer, String fieldName) {
if ( analyzer instanceof ConcurrentlyMutableAnalyzer ) {
ConcurrentlyMutableAnalyzer cma = (ConcurrentlyMutableAnalyzer) analyzer;
final Analyzer wrappedAnalyzer = cma.getWrappedAnalyzer( fieldName );
return wrappedAnalyzer.getReuseStrategy().getReusableComponents( wrappedAnalyzer, fieldName );
}
else {
throw new AssertionFailure( "This ReuseStrategy should only be applied to a ConcurrentlyMutableAnalyzer " );
}
}
@Override
public void setReusableComponents(Analyzer analyzer, String fieldName, TokenStreamComponents components) {
if ( analyzer instanceof ConcurrentlyMutableAnalyzer ) {
ConcurrentlyMutableAnalyzer cma = (ConcurrentlyMutableAnalyzer) analyzer;
final Analyzer wrappedAnalyzer = cma.getWrappedAnalyzer( fieldName );
wrappedAnalyzer.getReuseStrategy().setReusableComponents( analyzer, fieldName, components );
}
else {
throw new AssertionFailure( "This ReuseStrategy should only be applied to a ConcurrentlyMutableAnalyzer " );
}
}
};
}