package com.ora.jsp.tags.sql;
import java.util.*;
import java.sql.*;
import javax.sql.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import com.ora.jsp.sql.*;
import com.ora.jsp.sql.value.*;
/**
* This class is a custom action for executing a SQL SELECT statement.
* The statement must be defined in the body of the action. It can
* contain ? place holders, replaced by the value of <ora:sqlValue>
* elements before execution. The number and order of place holders must
* match the number and order of <ora:sqlValue> elements in the body.
*
* @author Hans Bergsten, Gefion software <hans@gefionsoftware.com>
* @version 1.0
*/
public abstract class DBTag extends BodyTagSupport implements ValueTagParent {
private SQLCommandBean sqlCommandBean = new SQLCommandBean();
private String dataSourceName;
private String id;
private int scope = PageContext.PAGE_SCOPE;
private String sqlValue;
private Vector values;
private boolean isExceptionThrown = false;
private boolean isPartOfTransaction = false;
/**
* Sets the dataSource attribute.
*
* @param dataSource the name of the DataSource object available
* in the application scope
*/
public void setDataSource(String dataSourceName) {
this.dataSourceName = dataSourceName;
}
/**
* Sets the id attribute.
*
* @param id the name for the result variable
*/
public void setId(String id) {
this.id = id;
}
/**
* Sets the scope attribute.
*
* @param scope the scope for the result variable
*/
public void setScope(String scopeName) {
if ("page".equals(scopeName)) {
scope = PageContext.PAGE_SCOPE;
}
else if ("request".equals(scopeName)) {
scope = PageContext.REQUEST_SCOPE;
}
else if ("session".equals(scopeName)) {
scope = PageContext.SESSION_SCOPE;
}
else if ("application".equals(scopeName)) {
scope = PageContext.APPLICATION_SCOPE;
}
}
/**
* Adds a value. This method is called by value tags in the
* action body.
*
* @param value a ValueHolder with a value for the PreparedStatement
*/
public void addValue(Value value) {
if (values == null) {
values = new Vector();
}
values.addElement(value);
}
/**
* Override the default implementation to reset the
* per-use state set by nested elements.
*/
public int doStartTag() {
values = null;
isExceptionThrown = false;
isPartOfTransaction = false;
return EVAL_BODY_TAG;
}
/**
* Sets the SQL statement to the contents of the element's
* body.
*/
public int doAfterBody() throws JspException {
sqlValue = bodyContent.getString();
return SKIP_BODY;
}
/**
* If the action is included in the body of a <ora:transaction> tag,
* gets the connection from the <ora:transaction> tag. If not,
* gets the DataSource from the application scope and asks for a Connection.
* Executes the SQL query defined in the action element's body, and saves the
* result as a Vector with Row objects in the specified scope under the
* specified name.
*
*/
public int doEndTag() throws JspException {
Connection conn = getConnection();
sqlCommandBean.setConnection(conn);
sqlCommandBean.setSqlValue(sqlValue);
sqlCommandBean.setValues(values);
Object result = null;
try {
result = execute(sqlCommandBean);
}
catch (SQLException e) {
try {
isExceptionThrown = true;
conn.rollback();
}
catch (SQLException se) {} // Ignore, probably a result of the main exception
throw new JspException("SQL error: " + e.getMessage());
}
catch (UnsupportedTypeException e) {
try {
isExceptionThrown = true;
conn.rollback();
}
catch (SQLException se) {} // Ignore, probably caused by the main exception
throw new JspException("Query result error: " + e.getMessage());
}
finally {
try {
if (isPartOfTransaction && isExceptionThrown) {
// Reset auto commit in case the connection is pooled
// before it's returned to the pool by close
conn.setAutoCommit(true);
conn.close();
}
else if (!isPartOfTransaction) {
// If we're not part of a transaction, the connection
// is in auto commit mode so we only close it
conn.close();
}
}
catch (SQLException e) {
e.printStackTrace(System.err);
}
}
// Save the result with the specified id in the specified scope
if (id != null) {
pageContext.setAttribute(id, result, scope);
}
return EVAL_PAGE;
}
/**
* Releases all instance variables.
*/
public void release() {
dataSourceName = null;
id = null;
scope = PageContext.PAGE_SCOPE;
sqlValue = null;
values = null;
isExceptionThrown = false;
isPartOfTransaction = false;
super.release();
}
/**
* Must be implemented by subclass to perform the appropriate
* processing.
*/
protected abstract Object execute(SQLCommandBean sqlCommandBean)
throws SQLException, UnsupportedTypeException;
/**
* Gets the Connection either from a transaction parent or from
* the DataSource specified for the action.
*/
private Connection getConnection() throws JspException {
Connection conn = null;
TransactionTag transactionTag = (TransactionTag)
findAncestorWithClass(this, TransactionTag.class);
if (transactionTag != null) {
conn = transactionTag.getConnection();
isPartOfTransaction = true;
if (dataSourceName != null) {
throw new JspException("A dataSource must not be specified " +
"when the action is part of a transaction");
}
}
else {
DataSource dataSource = (DataSource)
pageContext.getAttribute(dataSourceName, PageContext.APPLICATION_SCOPE);
if (dataSource == null) {
throw new JspException("dataSource " + dataSourceName + " not found");
}
try {
conn = dataSource.getConnection();
}
catch (SQLException e) {
throw new JspException("SQL error: " + e.getMessage());
}
}
return conn;
}
}
|