/*
* Copyright 2005 Werner Guttmann
*
* 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.exolab.castor.jdo.engine;
import java.sql.Connection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.castor.core.util.Messages;
import org.castor.persist.LocalTransactionContext;
import org.exolab.castor.jdo.DatabaseNotFoundException;
import org.exolab.castor.jdo.OQLQuery;
import org.exolab.castor.jdo.PersistenceException;
import org.exolab.castor.jdo.TransactionAbortedException;
import org.exolab.castor.jdo.TransactionNotInProgressException;
import org.exolab.castor.persist.spi.CallbackInterceptor;
import org.exolab.castor.persist.spi.InstanceFactory;
/**
* An implementation of the JDO {@link Database} interface supporting explicit local
* transaction demarcation.
*
* @author <a href="arkin@intalio.com">Assaf Arkin</a>
* @author <a href="mailto:ferret AT frii dot com">Bruce Snyder</a>
* @author <a href="mailto:werner DOT guttmann AT gmx DOT net">Werner Guttmann</a>
* @version $Revision$ $Date: 2006-04-10 16:39:24 -0600 (Mon, 10 Apr 2006) $
*/
public class LocalDatabaseImpl extends AbstractDatabaseImpl {
/** The <a href="http://jakarta.apache.org/commons/logging/">Jakarta
* Commons Logging</a> instance used for all logging. */
private static Log _log = LogFactory.getFactory().getInstance(LocalDatabaseImpl.class);
/**
* Creates an instance of this class.
* @param dbName database name
* @param lockTimeout Lock timeout
* @param callback {@link CallbackInterceptor} instance
* @param instanceFactory Instance factory.
* @param classLoader Current class loader
* @param autoStore Indicates whether to use 'auto-storing'
* @throws DatabaseNotFoundException If the specified database configuration cannot be found.
*/
public LocalDatabaseImpl(final String dbName,
final int lockTimeout,
final CallbackInterceptor callback,
final InstanceFactory instanceFactory,
final ClassLoader classLoader,
final boolean autoStore)
throws DatabaseNotFoundException {
super(dbName, lockTimeout, callback, instanceFactory, classLoader, autoStore);
_ctx = new LocalTransactionContext(this);
_ctx.setLockTimeout(_lockTimeout);
_ctx.setAutoStore(_autoStore);
_ctx.setCallback(_callback);
_ctx.setInstanceFactory(_instanceFactory);
_classLoader = classLoader;
}
/**
* @inheritDoc
* @see org.exolab.castor.jdo.Database#close()
*/
public synchronized void close() throws PersistenceException {
try {
if (isActive()) {
try {
_ctx.rollback();
} catch (Exception except) {
_log.debug("Exception at rollback of TransactionContext.");
}
try {
_ctx.close();
} catch (Exception except) {
_log.debug("Exception at close of TransactionContext.");
}
throw new PersistenceException(Messages.message("jdo.dbClosedTxRolledback"));
}
} finally {
_scope = null;
}
}
/**
* Overrides Object.finalize().
*
* Outputs a warning message to the logs if the current DatabaseImpl
* instance still has valid scope. In this condition - a condition that
* ideally should not occur at all - we close the instance as well to
* free up resources.
*
* @see java.lang.Object#finalize()
*/
protected void finalize() throws Throwable {
if (_scope != null || !isActive()) { return; }
// retrieve SQL bound to this Database instance
OQLQuery oqlQuery = getOQLQuery();
String sql = ((OQLQueryImpl) oqlQuery).getSQL();
_log.warn(Messages.format("jdo.finalize_close", this.toString(), _dbName, sql));
close();
}
/**
* @inheritDoc
*/
public void begin() throws PersistenceException {
_log.debug("Beginning tx");
if (isActive()) {
throw new PersistenceException(Messages.message("jdo.txInProgress"));
}
// _ctx.setStatus(Status.STATUS_ACTIVE);
_ctx.setStatus(0);
_ctx.setLockTimeout(_lockTimeout);
_ctx.setAutoStore(_autoStore);
_ctx.setCallback(_callback);
_ctx.setInstanceFactory(_instanceFactory);
registerSynchronizables();
}
/**
* @inheritDoc
*/
public void commit() throws TransactionNotInProgressException, TransactionAbortedException {
_log.debug("Committing tx");
if (!isActive()) {
throw new TransactionNotInProgressException(Messages.message("jdo.txNotInProgress"));
}
// if ( _ctx.getStatus() == Status.STATUS_MARKED_ROLLBACK )
if (_ctx.getStatus() == 1) {
throw new TransactionAbortedException(Messages.message("jdo.txRollback"));
}
try {
_ctx.prepare();
_ctx.commit();
} catch (TransactionAbortedException except) {
_log.info(Messages.format("jdo.txAborted", except.getMessage()), except);
_ctx.rollback();
throw except;
} finally {
try {
// TODO [SMH]: Temporary fix, see bug 1491/CASTOR-630.
if (_ctx.isOpen()) {
_ctx.close();
}
} catch (Exception e) {
_log.info(e.getMessage(), e);
}
}
unregisterSynchronizables();
}
/**
* @inheritDoc
* @see org.exolab.castor.jdo.Database#rollback()
*/
public void rollback() throws TransactionNotInProgressException {
_log.debug("Rolling back tx");
if (!isActive()) {
throw new TransactionNotInProgressException(Messages.message("jdo.txNotInProgress"));
}
_ctx.rollback();
unregisterSynchronizables();
}
/**
* @inheritDoc
* @see org.exolab.castor.jdo.Database#getJdbcConnection()
*/
public Connection getJdbcConnection() throws PersistenceException {
if (_ctx == null || !_ctx.isOpen()) {
String message = Messages.message("jdo.dbTxNotInProgress.jdbc");
throw new PersistenceException (message);
}
return _ctx.getConnection(_scope.getLockEngine());
}
}