
it.anyplace.sync.bep.IndexFinder Maven / Gradle / Ivy
/*
* Copyright 2017 Davide Imbriaco .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/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 it.anyplace.sync.bep;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Lists;
import com.google.common.collect.Queues;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import it.anyplace.sync.core.beans.FileInfo;
import it.anyplace.sync.core.interfaces.IndexRepository;
import it.anyplace.sync.core.utils.ExecutorUtils;
import static it.anyplace.sync.core.utils.FileInfoOrdering.ALPHA_ASC_DIR_FIRST;
import java.io.Closeable;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class IndexFinder implements Closeable {
private final Logger logger = LoggerFactory.getLogger(getClass());
private Comparator ordering = ALPHA_ASC_DIR_FIRST;
private final IndexRepository indexRepository;
private final ExecutorService queryExecutorService = Executors.newSingleThreadExecutor(), eventProcessingService = Executors.newCachedThreadPool();
private final EventBus eventBus = new AsyncEventBus(eventProcessingService);
private final boolean dropQueriesOnNewSubmit = true;
private final int maxResults;
// private final Queue runningQueries = Queues.newConcurrentLinkedQueue();
private Future previousQuery;
private IndexFinder(IndexRepository indexRepository, int maxResults) {
checkNotNull(indexRepository);
checkArgument(maxResults > 0);
this.indexRepository = indexRepository;
this.maxResults = maxResults;
}
public IndexFinder setOrdering(Comparator ordering) {
checkNotNull(ordering);
this.ordering = ordering;
return this;
}
public EventBus getEventBus() {
return eventBus;
}
public synchronized IndexFinder submitSearch(final String query) {
checkArgument(!StringUtils.isBlank(query), "query term cannot be blank");
logger.info("submitSearch, term = '{}'", query);
if (dropQueriesOnNewSubmit == true && previousQuery != null) {
// previousQuery.cancel(true); note: cannot interrupt. interrupt break h2 connection to db
previousQuery.cancel(false);
}
previousQuery = queryExecutorService.submit(new Runnable() {
@Override
public void run() {
try {
logger.info("search file info for query = '{}'", query);
final long count = indexRepository.countFileInfoBySearchTerm(query);
final boolean hasTooManyResults = count > maxResults, hasGoodResults = count > 0 && count <= maxResults;
final List list = hasGoodResults ? indexRepository.findFileInfoBySearchTerm(query) : null;
// final List list = indexRepository.findFileInfoBySearchTerm(query);
// final boolean hasTooManyResults = list.size() > maxResults, hasGoodResults = !list.isEmpty() && !hasTooManyResults;
logger.info("got {} results for search term = '{}'", count, query);
if (!eventProcessingService.isShutdown()) {
eventBus.post(new SearchCompletedEvent() {
@Override
public String getQuery() {
return query;
}
@Override
public List getResultList() {
checkNotNull(list, "this query has no good results (got either too many results or zero results)");
List res = Lists.newArrayList(list);
Collections.sort(list, ordering);
return res;
}
@Override
public long getResultCount() {
return count;
}
@Override
public boolean hasZeroResults() {
return count == 0;
}
@Override
public boolean hasTooManyResults() {
return hasTooManyResults;
}
@Override
public boolean hasGoodResults() {
return hasGoodResults;
}
});
}
} catch (Exception ex) {
if (Thread.currentThread().isInterrupted()) {
logger.warn("interrupted search for term = '{}', ex = {}", query, ex);
} else {
logger.error("error running file info search by term = '{}'", query);
logger.error("error running file info search by term", ex);
}
}
}
});
return this;
}
public SearchCompletedEvent doSearch(final String term) throws InterruptedException {
final Object lock = new Object();
final AtomicReference reference = new AtomicReference<>();
final Object eventListener = new Object() {
@Subscribe
public void handleSearchCompletedEvent(SearchCompletedEvent event) {
if (equal(event.getQuery(), term)) {
synchronized (lock) {
reference.set(event);
lock.notify();
}
}
}
};
synchronized (lock) {
eventBus.register(eventListener);
submitSearch(term);
try {
while (!Thread.currentThread().isInterrupted() && reference.get() == null) {
lock.wait();
}
checkNotNull(reference.get());
return reference.get();
} finally {
eventBus.unregister(eventListener);
}
}
}
public interface SearchCompletedEvent {
public String getQuery();
public long getResultCount();
public boolean hasZeroResults();
public boolean hasTooManyResults();
public boolean hasGoodResults();
public List getResultList();
}
@Override
public void close() {
queryExecutorService.shutdown();
eventProcessingService.shutdown();
ExecutorUtils.awaitTerminationSafe(eventProcessingService);
// ExecutorUtils.awaitTerminationSafe(executorService); note: not waiting for temination (fast close)
}
public static Builder newBuilder() {
return new Builder();
}
public static class Builder {
private IndexRepository indexRepository;
private int maxResults = 16;
private Builder() {
}
public IndexRepository getIndexRepository() {
return indexRepository;
}
public Builder setIndexRepository(IndexRepository indexRepository) {
this.indexRepository = indexRepository;
return this;
}
public int getMaxResults() {
return maxResults;
}
public Builder setMaxResults(int maxResults) {
this.maxResults = maxResults;
return this;
}
public IndexFinder build() {
return new IndexFinder(indexRepository, maxResults);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy