package dbControl; import org.apache.beehive.controls.api.ControlException; import org.apache.beehive.controls.api.bean.ControlImplementation; import org.apache.beehive.controls.api.bean.Extensible; import org.apache.beehive.controls.api.context.ControlBeanContext; import org.apache.beehive.controls.api.context.ResourceContext; import org.apache.beehive.controls.api.ControlException; import org.apache.beehive.controls.api.context.ResourceContext.ResourceEvents; import org.apache.beehive.controls.api.events.EventHandler; import dbControl.util.JavaTypeHelper; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Calendar; import java.util.Iterator; import java.util.HashMap; import java.util.Map; import java.util.Vector; import javax.naming.InitialContext; @ControlImplementation public class DatabaseControlImpl implements DatabaseControl, Extensible { //Error messages private static final String ERROR_GET_DATABASE_CONTROL = "Failed to obtain database control."; private static final String ERROR_MISSING_SQL_ATTRIBUTE = "SQL attribute not defined."; private static final String ERROR_MISSING_STATEMENT_MEMBER = "Statement member not defined in SQL attribute"; private static final String ERROR_ITERATOR_ELEMENT_TYPE_NOT_SPECIFIED = "Iterator element type is not specified."; private static final String ERROR_RESULT_NOT_MATCH_RETURN_TYPE = "The results of the SQL provided does not match return type of method"; private static final String ERROR_INVALID_ARGUMENT_NAME = "Invalid argument name in SQL statement"; private static final String ERROR_PARAMETER_SUBSTITUTION_FAILED = "Failed to substitute parameter by name"; @org.apache.beehive.controls.api.context.Context ControlBeanContext context; @org.apache.beehive.controls.api.context.Context ResourceContext resourceContext; transient Calendar calendar = null; transient Connection connection = null; Vector resources = new Vector(); public Calendar getDataSourceCalendar() { return calendar; } public void setDataSourceCalendar(Calendar cal) { this.calendar = (Calendar)cal.clone(); } public Connection getConnection() { if (this.connection == null) { DatabaseControl.ConnectionDataSource ds = (ConnectionDataSource)context.getControlPropertySet(ConnectionDataSource.class); if (ds == null) throw new ControlException("no @" + ConnectionDataSource.class.getName() + "\' property found."); //TODO: Need to change this code to use data source. try { Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); this.connection = DriverManager.getConnection(ds.jndiName()); } catch (Exception e) { throw new ControlException(ERROR_GET_DATABASE_CONTROL, e); } } return this.connection; } @EventHandler(field="resourceContext",eventSet=ResourceEvents.class, eventName="onRelease") public void onRelease() { for (int i = 0 ; i < this.resources.size() ; i++) { Object o = this.resources.get(i); if (o instanceof PreparedStatement) { try { ((PreparedStatement)o).close(); } catch (SQLException sqe) { //TODO: log error closing prepared statement. } } else { //TODO: log unexpected resource type } } resources.clear(); try { if (this.connection != null) this.connection.close(); } catch (SQLException sqe) { //TODO: log error closing connection } this.connection = null; } @EventHandler(field="resourceContext",eventSet=ResourceEvents.class, eventName="onAcquire") public void onAcquire() { try { getConnection(); } catch (ControlException e) { //TODO: log error } } /** * Extensible.invoke * Handles all extended interface methods */ public Object invoke(Method method, Object[] args) throws Throwable { //Retrieve the SQL statement from the STATEMENT member of the SQL attribute. SQL methodSQL = (SQL)context.getMethodPropertySet(method, SQL.class); if (methodSQL == null) throw new ControlException (ERROR_MISSING_SQL_ATTRIBUTE); String statement = methodSQL.statement(); if (statement == null || statement.length() == 0) throw new ControlException (ERROR_MISSING_STATEMENT_MEMBER); //Parse the SQL statement SQLParser parser = new SQLParser(statement); SQLStatement sql = parser.parse(); //Set the parameters of the SQL statement based on the method arguments //sql.setParameterValuesByPosition(args); this.setParameterValues(sql.getParameters(), method, args); PreparedStatement ps = sql.getPreparedStatement(this.getConnection()); //Set the max. rows returned int maxRows = methodSQL.maxRows(); if (maxRows != MAXROWS_ALL && maxRows > 0) ps.setMaxRows(maxRows); boolean hasResults = ps.execute(); // // process returned data // Class returnType = method.getReturnType(); Object returnObject = null; if (hasResults) { // If a result must live beyond the lifetime of this // 'invoke', the statement must be kept open and // closed later. ResultSet rs = ps.getResultSet(); if (returnType.equals(ResultSet.class)) { this.resources.add(ps); returnObject = rs; } else { ResultSetExtractor extractor = new ResultSetExtractor(rs, this.calendar); //return iterator if (returnType.equals(Iterator.class)) { this.resources.add(ps); Class iteratorElement = methodSQL.iteratorElementType(); if (null == iteratorElement) throw new Exception(ERROR_ITERATOR_ELEMENT_TYPE_NOT_SPECIFIED); returnObject = extractor.extractIterator(iteratorElement); } //return array else if (returnType.isArray()) { returnObject = extractor.extractArray(returnType); } //return HashMap else if (returnType.equals(HashMap.class)) { returnObject = extractor.extractHashMap(); } //return an unmodifiable view of the HashMap else if (returnType.equals(Map.class)) { returnObject = extractor.extractUnmodifiableMap(); } else { if (!rs.next()) //return null or if return type is a primitive, return the primitive's default value. returnObject = JavaTypeHelper.getJaveTypeDefaultValue(returnType); else //return object returnObject = extractor.extractObject(returnType); } } } else { if (returnType.equals(Void.TYPE)) returnObject = null; else if (!returnType.equals(Integer.TYPE)) throw new ControlException(ERROR_RESULT_NOT_MATCH_RETURN_TYPE); returnObject = new Integer(ps.getUpdateCount()); } return returnObject; } private void setParameterValues(SQLParameter[] params, Method method, Object[] args) { for (int i = 0; i < params.length; i++) { SQLParameter param = params[i]; String firstNameComponent = param.getFirstNameComponent(); try { Object value = context.getParameterValue(method, firstNameComponent, args); param.setValue(value); } catch (IllegalArgumentException iae) { throw new ControlException(ERROR_INVALID_ARGUMENT_NAME); } catch (Exception e) { throw new ControlException(ERROR_PARAMETER_SUBSTITUTION_FAILED, e); } } } }