/* * The contents of this file are subject to the Open Software License * Version 3.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.rosenlaw.com/OSL3.0.htm * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * This file is an original work developed by Netymon Pty Ltd * (http://www.netymon.com, mailto:mail@netymon.com). Portions created * by Netymon Pty Ltd are Copyright (c) 2006 Netymon Pty Ltd. * All Rights Reserved. */ package org.mulgara.resolver; // Third party packages import org.apache.log4j.Logger; // Local packages import org.mulgara.query.AbstractAnswer; import org.mulgara.query.Answer; import org.mulgara.query.MulgaraTransactionException; import org.mulgara.query.TuplesException; import org.mulgara.query.Variable; /** * A transactional answer. * Wraps all calls to the enclosed answer object, ensuring all calls are made * within an activated transactional context. Also ensures that that context * is deactivated upon returning from the outer-call. * * @created 2006-10-06 * * @author <a href="mailto:andrae@netymon.com">Andrae Muys</a> * * @version $Revision: $ * * @modified $Date: $ * * @maintenanceAuthor $Author: $ * * @company <A href="mailto:mail@netymon.com">Netymon Pty Ltd</A> * * @copyright ©2006 <a href="http://www.netymon.com/">Netymon Pty Ltd</a> * * @licence Open Software License v3.0</a> */ public class TransactionalAnswer extends AbstractAnswer implements Answer { /** Logger. */ private static final Logger logger = Logger.getLogger(TransactionalAnswer.class.getName()); private Answer answer; private MulgaraTransaction transaction; private boolean closing; public TransactionalAnswer(MulgaraTransaction transaction, Answer answer) throws TuplesException { try { report("Creating Answer"); if (transaction == null) { throw new IllegalArgumentException("Transaction null in TransactionalAnswer"); } else if (answer == null) { throw new IllegalArgumentException("Answer null in TransactionalAnswer"); } this.answer = answer; this.closing = false; this.transaction = transaction; transaction.reference(); report("Created Answer"); } catch (MulgaraTransactionException em) { throw new TuplesException("Failed to associate with transaction", em); } } public Object getObject(final int column) throws TuplesException { notClosed(); return transaction.execute(new AnswerOperation() { public void execute() throws TuplesException { returnObject(answer.getObject(column)); } }).getObject(); } public Object getObject(final String columnName) throws TuplesException { notClosed(); return transaction.execute(new AnswerOperation() { public void execute() throws TuplesException { returnObject(answer.getObject(columnName)); } }).getObject(); } public void beforeFirst() throws TuplesException { notClosed(); transaction.execute(new AnswerOperation() { public void execute() throws TuplesException { answer.beforeFirst(); } }); } public void close() throws TuplesException { report("Closing Answer"); if (closing) { report("Deferring close to enclosing call"); return; } try { notClosed(); closing = true; transaction.execute(new AnswerOperation() { public void execute() throws TuplesException { answer.close(); try { transaction.dereference(); } catch (MulgaraTransactionException em) { throw new TuplesException("Error dereferencing transaction", em); } } }); } finally { // !!FIXME: Note - We will need to add checks for null to all operations. closing = false; transaction = null; answer = null; // Note this permits the gc of the answer. report("Closed Answer"); } } public int getColumnIndex(final Variable column) throws TuplesException { notClosed(); return transaction.execute(new AnswerOperation() { public void execute() throws TuplesException { returnInt(answer.getColumnIndex(column)); } }).getInt(); } public int getNumberOfVariables() { try { notClosed(); return transaction.execute(new AnswerOperation() { public void execute() { returnInt(answer.getNumberOfVariables()); } }).getInt(); } catch (TuplesException et) { throw new IllegalStateException(et.getMessage(), et); } } public Variable[] getVariables() { try { notClosed(); return (Variable[])(transaction.execute(new AnswerOperation() { public void execute() { returnObject(answer.getVariables()); } }).getObject()); } catch (TuplesException et) { throw new IllegalStateException(et.getMessage(), et); } } public boolean isUnconstrained() throws TuplesException { notClosed(); return transaction.execute(new AnswerOperation() { public void execute() throws TuplesException { returnBoolean(answer.isUnconstrained()); } }).getBoolean(); } public long getRowCount() throws TuplesException { notClosed(); return transaction.execute(new AnswerOperation() { public void execute() throws TuplesException { returnLong(answer.getRowCount()); } }).getLong(); } public long getRowUpperBound() throws TuplesException { notClosed(); return transaction.execute(new AnswerOperation() { public void execute() throws TuplesException { returnLong(answer.getRowUpperBound()); } }).getLong(); } public long getRowExpectedCount() throws TuplesException { notClosed(); return transaction.execute(new AnswerOperation() { public void execute() throws TuplesException { returnLong(answer.getRowExpectedCount()); } }).getLong(); } public int getRowCardinality() throws TuplesException { notClosed(); return transaction.execute(new AnswerOperation() { public void execute() throws TuplesException { returnInt(answer.getRowCardinality()); } }).getInt(); } public boolean isEmpty() throws TuplesException { notClosed(); return transaction.execute(new AnswerOperation() { public void execute() throws TuplesException { returnBoolean(answer.isEmpty()); } }).getBoolean(); } public boolean next() throws TuplesException { notClosed(); return transaction.execute(new AnswerOperation() { public void execute() throws TuplesException { returnBoolean(answer.next()); } }).getBoolean(); } public Object clone() { try { TransactionalAnswer c = (TransactionalAnswer)super.clone(); c.answer = (Answer)this.answer.clone(); c.transaction.reference(); report("Cloned Answer, clone=" + System.identityHashCode(c)); return c; } catch (MulgaraTransactionException em) { throw new IllegalStateException("Failed to associate with transaction", em); } } public boolean equals(Object o) { return super.equals(o); } public int hashCode() { return System.identityHashCode(this); } private void report(String desc) { if (logger.isDebugEnabled()) { logger.debug(desc + ": " + System.identityHashCode(this) + ", xa=" + System.identityHashCode(transaction)); } } @SuppressWarnings("unused") private void warnReport(String desc) { logger.warn(desc + ": " + System.identityHashCode(this) + ", xa=" + System.identityHashCode(transaction)); } public void finalize() throws Throwable { try { report("GC-finalizing"); if (transaction != null) { logger.warn("TransactionalAnswer not closed"); /* try { transaction.execute(new AnswerOperation() { public void execute() throws TuplesException { try { answer.close(); } finally { try { transaction.dereference(); } catch (MulgaraTransactionException em) { throw new TuplesException("Error dereferencing transaction", em); } } } }); } catch (TuplesException et) { report("Error dereferencing transaction from finalize"); } finally { transaction = null; answer = null; } */ // try { // sessionClose(); // } catch (TuplesException et) { // logger.warn("Error force-closing TransactionAnswer", et); // } } } finally { super.finalize(); } } void sessionClose() throws TuplesException { if (closing) { report("Session close on closing answer"); return; } if (answer != null) { report("Session forced close"); closing = true; try { answer.close(); } catch (TuplesException e) { throw e; } catch (Throwable th) { throw new TuplesException("Error closing answer", th); } finally { try { transaction.dereference(); } catch (MulgaraTransactionException em) { throw new TuplesException("Error dereferencing transaction", em); } finally { closing = false; answer = null; transaction = null; } } // close(); } } private void notClosed() throws TuplesException { if (transaction == null) { throw new TuplesException("TransactionalAnswer closed"); } else if (answer == null) { throw new TuplesException("TransactionAnswer not closed, but Answer null"); } } }