org.compass.core.lucene.engine.transaction.support.AbstractJobBasedTransactionProcessor Maven / Gradle / Ivy
/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-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 org.compass.core.lucene.engine.transaction.support;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.lucene.store.Lock;
import org.compass.core.Resource;
import org.compass.core.engine.SearchEngineException;
import org.compass.core.lucene.engine.LuceneSearchEngine;
import org.compass.core.lucene.engine.LuceneSearchEngineHits;
import org.compass.core.lucene.engine.LuceneSearchEngineInternalSearch;
import org.compass.core.lucene.engine.LuceneSearchEngineQuery;
import org.compass.core.lucene.engine.transaction.support.job.CreateTransactionJob;
import org.compass.core.lucene.engine.transaction.support.job.DeleteByQueryTransactionJob;
import org.compass.core.lucene.engine.transaction.support.job.DeleteTransactionJob;
import org.compass.core.lucene.engine.transaction.support.job.TransactionJob;
import org.compass.core.lucene.engine.transaction.support.job.TransactionJobs;
import org.compass.core.lucene.engine.transaction.support.job.UpdateTransactionJob;
import org.compass.core.spi.InternalResource;
import org.compass.core.spi.ResourceKey;
/**
* Base class for jobs based ({@link org.compass.core.lucene.engine.transaction.support.job.TransactionJobs})
* transaction processor. Mainly used for simple accumelating of {@link org.compass.core.lucene.engine.transaction.support.job.TransactionJob}s
* and then processing them during commit/rollback time.
*
* Allows for optionl ordering guarantees of transaction operations across several concurrent transactions from the
* same or from different JVMs. The setting name to control it is maintainOrder
and the constructor allows
* to control its default value.
*
*
Note, if only search based operations were performed, then prepare/commit/rollback will not be called.
*
* @author kimchy
*/
public abstract class AbstractJobBasedTransactionProcessor extends AbstractSearchTransactionProcessor {
private TransactionJobs jobs;
private final boolean maintainOrder;
private final Map orderLocks;
/**
* Constructs a new job based transction processor. The defaultMaintainOrderLock
will control if, by
* default when no setting is specified (setting name is maintainOrder
), what will be the default value
* of it.
*/
public AbstractJobBasedTransactionProcessor(Log logger, LuceneSearchEngine searchEngine, boolean defaultMaintainOrderLock) {
super(logger, searchEngine);
maintainOrder = searchEngine.getSettings().getSettingAsBoolean(getSettingName("maintainOrder"), defaultMaintainOrderLock);
if (maintainOrder) {
orderLocks = new HashMap();
} else {
orderLocks = null;
}
}
public void begin() throws SearchEngineException {
// nothing to do here
}
public void prepare() throws SearchEngineException {
if (jobs == null) {
return;
}
doPrepare(jobs);
}
abstract protected void doPrepare(TransactionJobs jobs) throws SearchEngineException;
public void commit(boolean onePhase) throws SearchEngineException {
if (jobs == null) {
return;
}
try {
doCommit(onePhase, jobs);
} finally {
clearLocksIfNeeded();
}
}
abstract protected void doCommit(boolean onePhase, TransactionJobs jobs) throws SearchEngineException;
public void rollback() throws SearchEngineException {
if (jobs == null) {
return;
}
try {
doRollback(jobs);
} finally {
clearLocksIfNeeded();
}
}
abstract protected void doRollback(TransactionJobs jobs) throws SearchEngineException;
public void create(InternalResource resource) throws SearchEngineException {
obtainOrderLockIfNeeded(resource.getSubIndex());
getTransactionJobs().add(new CreateTransactionJob(resource));
}
public void update(InternalResource resource) throws SearchEngineException {
obtainOrderLockIfNeeded(resource.getSubIndex());
getTransactionJobs().add(new UpdateTransactionJob(resource));
}
public void delete(ResourceKey resourceKey) throws SearchEngineException {
obtainOrderLockIfNeeded(resourceKey.getSubIndex());
getTransactionJobs().add(new DeleteTransactionJob(resourceKey));
}
public void delete(LuceneSearchEngineQuery query) throws SearchEngineException {
flush();
String[] calcSubIndexes = indexManager.getStore().calcSubIndexes(query.getSubIndexes(), query.getAliases());
for (String subIndex : calcSubIndexes) {
obtainOrderLockIfNeeded(subIndex);
getTransactionJobs().add(new DeleteByQueryTransactionJob(query.getQuery(), subIndex));
}
}
public void flush() throws SearchEngineException {
// nothing to do here
}
/**
* Calls {@link #doFlushCommit(org.compass.core.lucene.engine.transaction.support.job.TransactionJobs)} and then
* clears the jobs listed to be processed.
*/
public void flushCommit(String ... aliases) throws SearchEngineException {
flush();
if (aliases == null || aliases.length == 0) {
doFlushCommit(jobs);
jobs = null;
} else {
Set subIndexes = new HashSet(Arrays.asList(indexManager.polyCalcSubIndexes(null, aliases, null)));
TransactionJobs flushJobs = new TransactionJobs();
TransactionJobs leftoverJobs = new TransactionJobs();
for (TransactionJob job : jobs.getJobs()) {
if (subIndexes.contains(job.getSubIndex())) {
flushJobs.add(job);
} else {
leftoverJobs.add(job);
}
}
doFlushCommit(flushJobs);
jobs = leftoverJobs;
}
}
protected abstract void doFlushCommit(TransactionJobs jobs);
public LuceneSearchEngineHits find(LuceneSearchEngineQuery query) throws SearchEngineException {
return performFind(query);
}
public LuceneSearchEngineInternalSearch internalSearch(String[] subIndexes, String[] aliases) throws SearchEngineException {
return performInternalSearch(subIndexes, aliases);
}
public Resource[] get(ResourceKey resourceKey) throws SearchEngineException {
return performGet(resourceKey);
}
private TransactionJobs getTransactionJobs() {
if (jobs == null) {
jobs = new TransactionJobs();
}
return jobs;
}
private void obtainOrderLockIfNeeded(String subIndex) throws SearchEngineException {
if (!maintainOrder) {
return;
}
Lock lock = orderLocks.get(subIndex);
if (lock == null) {
lock = searchEngine.getSearchEngineFactory().getLuceneIndexManager().getStore().openDirectory(subIndex).makeLock("order.lock");
try {
lock.obtain(searchEngine.getSearchEngineFactory().getLuceneSettings().getTransactionLockTimout());
} catch (IOException e) {
clearLocksIfNeeded();
throw new SearchEngineException("Failed to obtain order lock on sub index [" + subIndex + "]", e);
}
orderLocks.put(subIndex, lock);
}
}
private void clearLocksIfNeeded() {
if (!maintainOrder) {
return;
}
for (Map.Entry entry : orderLocks.entrySet()) {
try {
entry.getValue().release();
} catch (IOException e) {
logger.warn("Failed to release lock for sub index [" + entry.getKey() + "]", e);
}
}
orderLocks.clear();
}
}