You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beehive.apache.org by ek...@apache.org on 2005/05/09 22:18:03 UTC

svn commit: r169351 [5/12] - in /incubator/beehive/trunk/system-controls: ./ ant/ external/ external/commons/ external/ejb/ external/jms/ src/ src/ejb/ src/ejb/org/ src/ejb/org/apache/ src/ejb/org/apache/beehive/ src/ejb/org/apache/beehive/controls/ src/ejb/org/apache/beehive/controls/system/ src/ejb/org/apache/beehive/controls/system/ejb/ src/ejb/schema/ src/jdbc/ src/jdbc/org/ src/jdbc/org/apache/ src/jdbc/org/apache/beehive/ src/jdbc/org/apache/beehive/controls/ src/jdbc/org/apache/beehive/controls/system/ src/jdbc/org/apache/beehive/controls/system/jdbc/ src/jdbc/org/apache/beehive/controls/system/jdbc/parser/ src/jms/ src/jms/org/ src/jms/org/apache/ src/jms/org/apache/beehive/ src/jms/org/apache/beehive/controls/ src/jms/org/apache/beehive/controls/system/ src/jms/org/apache/beehive/controls/system/jms/ src/jms/org/apache/beehive/controls/system/jms/impl/ src/jms/org/apache/beehive/controls/system/jndi/ src/jms/org/apache/beehive/controls/system/jndi/impl/ src/webservice/ src/webservice/org/ src/webservice/org/apache/ src/webservice/org/apache/beehive/ src/webservice/org/apache/beehive/controls/ src/webservice/org/apache/beehive/controls/system/ src/webservice/org/apache/beehive/controls/system/webservice/ src/webservice/org/apache/beehive/controls/system/webservice/generator/ src/webservice/org/apache/beehive/controls/system/webservice/jaxrpc/ src/webservice/org/apache/beehive/controls/system/webservice/utils/ test/ test/ant/ test/conf/ test/src/ test/src/jdbc/ test/src/jdbc/controls/ test/src/jdbc/controls/org/ test/src/jdbc/controls/org/apache/ test/src/jdbc/controls/org/apache/beehive/ test/src/jdbc/controls/org/apache/beehive/controls/ test/src/jdbc/controls/org/apache/beehive/controls/system/ test/src/jdbc/controls/org/apache/beehive/controls/system/jdbc/ test/src/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/ test/src/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/dbconnection/ test/src/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/errors/ test/src/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/results/ test/src/jdbc/controls/schemas/ test/src/jdbc/controls/schemas/badusers/ test/src/jdbc/controls/schemas/users/ test/src/jdbc/jdbc-container/ test/src/jdbc/jdbc-container/application/ test/src/jdbc/jdbc-container/src/ test/src/jdbc/jdbc-container/src/org/ test/src/jdbc/jdbc-container/src/org/apache/ test/src/jdbc/jdbc-container/src/org/apache/beehive/ test/src/jdbc/jdbc-container/src/org/apache/beehive/controls/ test/src/jdbc/jdbc-container/src/org/apache/beehive/controls/system/ test/src/jdbc/jdbc-container/src/org/apache/beehive/controls/system/jdbc/ test/src/jdbc/jdbc-container/src/org/apache/beehive/controls/system/jdbc/containertest/ test/src/jdbc/jdbc-container/webapp/ test/src/jdbc/jdbc-container/webapp/WEB-INF/ test/src/jdbc/jdbc-container/webapp/conf/ test/src/jdbc/jdbc-container/webapp/conf/Catalina/ test/src/jdbc/jdbc-container/webapp/conf/Catalina/localhost/ test/src/jdbc/junitTests/ test/src/jdbc/junitTests/org/ test/src/jdbc/junitTests/org/apache/ test/src/jdbc/junitTests/org/apache/beehive/ test/src/jdbc/junitTests/org/apache/beehive/controls/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/dbconnection/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/errors/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/results/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/sqlparser/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/utils/

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/JdbcControlChecker.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/JdbcControlChecker.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/JdbcControlChecker.java (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/JdbcControlChecker.java Mon May  9 13:17:58 2005
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * 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.
+ *
+ * $Header:$
+ */
+
+package org.apache.beehive.controls.system.jdbc;
+
+import com.sun.mirror.apt.AnnotationProcessorEnvironment;
+import com.sun.mirror.declaration.Declaration;
+import com.sun.mirror.declaration.FieldDeclaration;
+import com.sun.mirror.declaration.MethodDeclaration;
+import com.sun.mirror.declaration.TypeDeclaration;
+import com.sun.mirror.type.ArrayType;
+import com.sun.mirror.type.DeclaredType;
+import com.sun.mirror.type.InterfaceType;
+import com.sun.mirror.type.MirroredTypeException;
+import com.sun.mirror.type.PrimitiveType;
+import com.sun.mirror.type.TypeMirror;
+import com.sun.mirror.type.VoidType;
+import org.apache.beehive.controls.api.ControlException;
+import org.apache.beehive.controls.api.bean.ControlChecker;
+import org.apache.beehive.controls.system.jdbc.parser.ParameterChecker;
+import org.apache.beehive.controls.system.jdbc.parser.SqlParser;
+import org.apache.beehive.controls.system.jdbc.parser.SqlStatement;
+
+import java.util.Collection;
+import java.sql.SQLException;
+
+/**
+ * Annotation checker for the JdbcControl.  Invoked at compile time by the controls framework.
+ */
+public class JdbcControlChecker implements ControlChecker {
+
+    /**
+     * Invoked by the control build-time infrastructure to process a declaration of
+     * a control extension (ie, an interface annotated with @ControlExtension), or
+     * a field instance of a control type.
+     */
+    public void check(Declaration decl, AnnotationProcessorEnvironment env) {
+
+        if (decl instanceof TypeDeclaration) {
+
+            //
+            // Check class annotations
+            //
+            checkConnectionDataSource((TypeDeclaration) decl, env);
+            checkConnectionDriver((TypeDeclaration) decl, env);
+            checkConnectionOptions((TypeDeclaration) decl, env);
+
+            //
+            // Check method annotations
+            //
+            Collection<? extends MethodDeclaration> methods = ((TypeDeclaration) decl).getMethods();
+            for (MethodDeclaration method : methods) {
+                checkSQL(method, env);
+
+            }
+        } else if (decl instanceof FieldDeclaration) {
+
+            //
+            // NOOP
+            //
+        } else {
+
+            //
+            // NOOP
+            //
+        }
+    }
+
+    /**
+     * Check the ConnectionDataSource annotation.
+     *
+     * @param decl
+     * @param env
+     */
+    private void checkConnectionDataSource(TypeDeclaration decl, AnnotationProcessorEnvironment env) {
+        final JdbcControl.ConnectionDataSource cds =
+                decl.getAnnotation(JdbcControl.ConnectionDataSource.class);
+        if (cds == null) {
+            return;
+        }
+
+        //
+        // NOOP
+        //
+    }
+
+    /**
+     * Check the ConnectionDriver annotation.
+     *
+     * @param decl
+     * @param env
+     */
+    private void checkConnectionDriver(TypeDeclaration decl, AnnotationProcessorEnvironment env) {
+        final JdbcControl.ConnectionDriver cd = decl.getAnnotation(JdbcControl.ConnectionDriver.class);
+        if (cd == null) {
+            return;
+        }
+
+        //
+        // NOOP
+        //
+    }
+
+    /**
+     * Check the ConnectionOptions annotation.
+     *
+     * @param decl
+     * @param env
+     */
+    private void checkConnectionOptions(TypeDeclaration decl, AnnotationProcessorEnvironment env) {
+        final JdbcControl.ConnectionOptions co = decl.getAnnotation(JdbcControl.ConnectionOptions.class);
+        if (co == null) {
+            return;
+        }
+
+        //
+        // NOOP
+        //
+    }
+
+    /**
+     * Check the SQL method annotation.  Lots to check here, stop checking as soon as an error is found.
+     *
+     * @param method
+     * @param env
+     */
+    private void checkSQL(MethodDeclaration method, AnnotationProcessorEnvironment env) {
+
+        final JdbcControl.SQL methodSQL = method.getAnnotation(JdbcControl.SQL.class);
+        if (methodSQL == null) {
+            return;
+        }
+
+        //
+        // check for empty SQL statement member
+        //
+        if (methodSQL.statement() == null || methodSQL.statement().length() == 0) {
+            env.getMessager().printError(method.getPosition(), "@SQL annotation on method: " + method.getSimpleName()
+                                                               + " contains an empty statement member.");
+            return;
+        }
+
+        //
+        // Make sure maxrows is not set to some negative number other than -1
+        //
+        int maxRows = methodSQL.maxRows();
+        if (maxRows < JdbcControl.MAXROWS_ALL) {
+            env.getMessager().printError(method.getPosition(), "@SQL annotation on method: " + method.getSimpleName()
+                                                               + " maxRows set to invalid value: " + maxRows);
+            return;
+        }
+
+        //
+        // Make sure maxArrayLength is not set to some negative number
+        //
+        int arrayMax = methodSQL.arrayMaxLength();
+        if (arrayMax < 1) {
+            env.getMessager().printError(method.getPosition(), "@SQL annotation on method: " + method.getSimpleName()
+                                                               + " arrayMaxLength set to invalid value (must be greater than 0): "
+                                                               + arrayMax);
+            return;
+        }
+
+
+        //
+        //
+        // parse the SQL
+        //
+        //
+        SqlParser _p = new SqlParser();
+        SqlStatement _statement = null;
+        try {
+            _statement = _p.parse(methodSQL.statement());
+        } catch (ControlException ce) {
+            env.getMessager().printError(method.getPosition(), "Error parsing SQL statment on method: " + method.getSimpleName() + ": " + ce.toString());
+            return;
+        }
+
+        //
+        // Check that the any statement element params (delimited by '{' and '}' can be
+        // matched to method parameter names. NOTE: This check is only valid on non-compiled files,
+        // once compiled to a class file method parameter names are replaced with 'arg0', 'arg1', etc.
+        // and cannot be used for this check.
+        //
+        try {
+            ParameterChecker.checkReflectionParameters(_statement, method);
+        } catch (ControlException e) {
+            env.getMessager().printError(method.getPosition(), e.getMessage());
+            return;
+        }
+
+        //
+        // check for case of generatedKeyColumns being set, when getGeneratedKeys is not set to true
+        //
+        final boolean getGeneratedKeys = methodSQL.getGeneratedKeys();
+        final String[] generatedKeyColumnNames = methodSQL.generatedKeyColumnNames();
+        final int[] generatedKeyIndexes = methodSQL.generatedKeyColumnIndexes();
+        if (getGeneratedKeys == false && (generatedKeyColumnNames.length != 0 || generatedKeyIndexes.length != 0)) {
+            env.getMessager().printError(method.getPosition(), "@SQL annotation on method: " + method.getSimpleName()
+                                                               + " getGeneratedKeys must be set to true in order to specify generatedKeyColumnNames or generatedKeyColumnIndexes.");
+            return;
+        }
+
+        //
+        // check that both generatedKeyColumnNames and generatedKeyColumnIndexes are not set
+        if (generatedKeyColumnNames.length > 0 && generatedKeyIndexes.length > 0) {
+            env.getMessager().printError(method.getPosition(), "@SQL annotation on method: " + method.getSimpleName()
+                                                               + " only one of generatedKeyColumnNames or generatedKeyColumnIndexes may be set in the method annotation.");
+
+            return;
+        }
+
+        //
+        // batch update methods must return int[]
+        //
+        final boolean batchUpdate = methodSQL.batchUpdate();
+        final TypeMirror returnType = method.getReturnType();
+        if (batchUpdate) {
+            if (returnType instanceof ArrayType == true) {
+                final TypeMirror aType = ((ArrayType) returnType).getComponentType();
+                if (aType instanceof PrimitiveType == false || ((PrimitiveType) aType).getKind() != PrimitiveType.Kind.INT) {
+                    env.getMessager().printError(method.getPosition(), "@SQL annotation on method: " + method.getSimpleName()
+                                                                       + " if batchUpdate is set to true, method must return void or an array of integers.");
+                    return;
+                }
+            } else if (returnType instanceof VoidType == false) {
+                env.getMessager().printError(method.getPosition(), "@SQL annotation on method: " + method.getSimpleName()
+                                                                   + " if batchUpdate is set to true, method must return void or an array of integers.");
+
+                return;
+            }
+
+        }
+
+        //
+        // iterator type check match
+        //
+        if (returnType instanceof InterfaceType) {
+            String iName = ((InterfaceType) returnType).getDeclaration().getQualifiedName();
+            if ("java.util.Iterator".equals(iName)) {
+                String iteratorClassName = null;
+                try {
+                    // this should always except
+                    methodSQL.iteratorElementType();
+                } catch (MirroredTypeException mte) {
+                    iteratorClassName = mte.getQualifiedName();
+                }
+
+                if ("org.apache.beehive.controls.system.jdbc.JdbcControl.UndefinedIteratorType".equals(iteratorClassName)) {
+                    env.getMessager().printError(method.getPosition(), "@SQL annotation on method: " + method.getSimpleName()
+                                                                       + " iteratorElementType must be specified if method returns an Iterator.");
+                    return;
+
+                }
+            }
+        }
+
+        //
+        // scrollable result set check
+        //
+        final JdbcControl.ScrollType scrollable = methodSQL.scrollableResultSet();
+        switch (scrollable) {
+            case SCROLL_INSENSITIVE:
+            case SCROLL_SENSITIVE:
+            case SCROLL_INSENSITIVE_UPDATABLE:
+            case SCROLL_SENSITIVE_UPDATABLE:
+            case FORWARD_ONLY_UPDATABLE:
+                String typeName = null;
+                if (returnType instanceof DeclaredType) {
+                    typeName = ((DeclaredType) returnType).getDeclaration().getQualifiedName();
+                }
+
+                if (typeName == null || "java.sql.ResultSet".equals(typeName) == false) {
+                    env.getMessager().printError(method.getPosition(), "@SQL annotation on method: " + method.getSimpleName()
+                                                                       + " element scrollableResultSet specified but method does not return a ResultSet.");
+                    break;
+                }
+            case FORWARD_ONLY:
+            default:
+                break;
+        }
+    } // checkSQL
+}

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/JdbcControlImpl.jcs
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/JdbcControlImpl.jcs?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/JdbcControlImpl.jcs (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/JdbcControlImpl.jcs Mon May  9 13:17:58 2005
@@ -0,0 +1,490 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * 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.
+ *
+ * $Header:$
+ */
+package org.apache.beehive.controls.system.jdbc;
+
+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.context.ResourceContext.ResourceEvents;
+import org.apache.beehive.controls.api.events.EventHandler;
+import org.apache.log4j.Logger;
+import org.apache.beehive.controls.system.jdbc.parser.SqlParser;
+import org.apache.beehive.controls.system.jdbc.parser.SqlStatement;
+
+import javax.naming.NamingException;
+import javax.naming.Context;
+import javax.sql.DataSource;
+import java.lang.reflect.Method;
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Vector;
+
+/**
+ * The implementation class for the database controller.
+ */
+@ControlImplementation
+public class JdbcControlImpl implements JdbcControl, Extensible, java.io.Serializable {
+
+    //
+    // contexts provided by the beehive controls runtime
+    //
+    @org.apache.beehive.controls.api.context.Context
+    protected ControlBeanContext _context;
+    @org.apache.beehive.controls.api.context.Context
+    protected ResourceContext _resourceContext;
+
+    protected transient Connection _connection;
+    protected transient ConnectionDataSource _connectionDataSource;
+    protected transient DataSource _dataSource;
+    protected transient ConnectionDriver _connectionDriver;
+
+    private transient Calendar _cal;
+    private transient Vector<PreparedStatement> _resources = new Vector<PreparedStatement>();
+
+    private static final String EMPTY_STRING = "";
+    private static final Logger logger = Logger.getLogger(JdbcControlImpl.class);
+    private static final ResultSetMapper DEFAULT_MAPPER = new DefaultObjectResultSetMapper();
+    private static final SqlParser _sqlParser = new SqlParser();
+
+    protected static final HashMap<Class, ResultSetMapper> _resultMappers = new HashMap<Class, ResultSetMapper>();
+    protected static Class<?> _xmlObjectClass;
+
+    //
+    // initialize the result mapper table
+    //
+    static {
+        _resultMappers.put(ResultSet.class, new DefaultResultSetMapper());
+        _resultMappers.put(Iterator.class, new DefaultIteratorResultSetMapper());
+
+        try {
+            _xmlObjectClass = Class.forName("org.apache.xmlbeans.XmlObject");
+            _resultMappers.put(_xmlObjectClass, new DefaultXmlObjectResultSetMapper());
+        } catch (ClassNotFoundException e) {
+            // noop: OK if not found, just can't support mapping to an XmlObject
+        }
+    }
+
+    /**
+     * Constructor
+     */
+    public JdbcControlImpl() { }
+
+    /**
+     * Invoked by the controls runtime when a new instance of this class is aquired by the runtime
+     */
+    @EventHandler(field = "_resourceContext", eventSet = ResourceEvents.class, eventName = "onAcquire")
+    public void onAquire() {
+        try {
+            getConnection();
+        } catch (SQLException se) {
+            throw new ControlException("SQL Exception while attempting to connect to database.", se);
+        }
+    }
+
+    /**
+     * Invoked by the controls runtime when an instance of this class is released by the runtime
+     */
+    @EventHandler(field = "_resourceContext", eventSet = ResourceContext.ResourceEvents.class, eventName = "onRelease")
+    public void onRelease() {
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("onRelease() invoked.");
+        }
+
+        for (PreparedStatement ps : _resources) {
+            try {
+                ps.close();
+            } catch (SQLException sqe) {
+            }
+        }
+        _resources.clear();
+
+        if (_connection != null) {
+            try {
+                _connection.close();
+            } catch (SQLException e) {
+                throw new ControlException("SQL Exception while attempting to close database connection.", e);
+            }
+        }
+
+        _connection = null;
+        _connectionDataSource = null;
+        _connectionDriver = null;
+    }
+
+    /**
+     * Returns a database connection to the server associated with the control.
+     * The connection type is specified by a ConnectionDataSource or ConnectionDriver annotation on the control class
+     * which extends this control.
+     * <p/>
+     * It is typically not necessary to call this method when using the control.
+     */
+    public Connection getConnection() throws SQLException {
+
+        if (_connection == null) {
+
+            _connectionDataSource = _context.getControlPropertySet(ConnectionDataSource.class);
+            _connectionDriver = _context.getControlPropertySet(ConnectionDriver.class);
+            final ConnectionOptions connectionOptions = _context.getControlPropertySet(ConnectionOptions.class);
+
+            if (_connectionDataSource != null && _connectionDataSource.jndiName() != null) {
+                _connection = getConnectionFromDataSource(_connectionDataSource.jndiName(),
+                                                          _connectionDataSource.jndiContextFactory());
+
+            } else if (_connectionDriver != null && _connectionDriver.databaseDriverClass() != null) {
+                _connection = getConnectionFromDriverManager(_connectionDriver.databaseDriverClass(),
+                                                             _connectionDriver.databaseURL(),
+                                                             _connectionDriver.userName(),
+                                                             _connectionDriver.password(),
+                                                             _connectionDriver.properties());
+            } else {
+                throw new ControlException("no @\'" + ConnectionDataSource.class.getName()
+                                           + "\' or \'" + ConnectionDriver.class.getName() + "\' property found.");
+            }
+
+            //
+            // set any specifed connection options
+            //
+            if (connectionOptions != null) {
+
+                if (_connection.isReadOnly() != connectionOptions.readOnly()) {
+                    _connection.setReadOnly(connectionOptions.readOnly());
+                }
+
+                DatabaseMetaData dbMetadata = _connection.getMetaData();
+
+                final HoldabilityType holdability = connectionOptions.resultSetHoldability();
+                if (holdability != HoldabilityType.DRIVER_DEFAULT) {
+                    if (dbMetadata.supportsResultSetHoldability(holdability.getHoldability())) {
+                        _connection.setHoldability(holdability.getHoldability());
+                    } else {
+                        throw new ControlException("Database does not support ResultSet holdability type: "
+                                                   + holdability.toString());
+                    }
+                }
+
+                setTypeMappers(connectionOptions.typeMappers());
+            }
+        }
+
+        return _connection;
+    }
+
+
+    /**
+     * Called by the Controls runtime to handle calls to methods of an extensible control.
+     *
+     * @param method The extended operation that was called.
+     * @param args   Parameters of the operation.
+     * @return The value that should be returned by the operation.
+     * @throws Throwable any exception declared on the extended operation may be
+     *                   thrown.  If a checked exception is thrown from the implementation that is not declared
+     *                   on the original interface, it will be wrapped in a ControlException.
+     */
+    public Object invoke(Method method, Object[] args) throws Throwable {
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Enter: invoke()");
+        }
+        assert _connection.isClosed() == false : "invoke(): JDBC Connection has been closed!!!!";
+        return execPreparedStatement(method, args);
+    }
+
+    /**
+     * Sets the calendar used when working with time/date types
+     */
+    public void setDataSourceCalendar(Calendar cal) {
+        _cal = (Calendar) cal.clone();
+    }
+
+    /**
+     * Returns the calendar used when working with time/date types.
+     *
+     * @return
+     */
+    public Calendar getDataSourceCalendar() {
+        return _cal;
+    }
+
+
+// /////////////////////////////////////////// Protected Methods ////////////////////////////////////////////
+
+
+    /**
+     * Create and exec the query
+     *
+     * @param method
+     * @param args
+     * @return
+     * @throws Throwable
+     */
+    protected Object execPreparedStatement(Method method, Object[] args)
+            throws Throwable {
+
+        final SQL methodSQL = (SQL) _context.getMethodPropertySet(method, SQL.class);
+        if (methodSQL == null || methodSQL.statement() == null) {
+            throw new ControlException("Method " + method.getName() + " is missing @SQL annotation");
+        }
+
+        setTypeMappers(methodSQL.typeMappersOverride());
+
+        //
+        // build the statement and execute it
+        //
+
+        PreparedStatement ps = null;
+        try {
+            Class returnType = method.getReturnType();
+
+            SqlStatement sqlStatement = _sqlParser.parse(methodSQL.statement());
+            ps = sqlStatement.createPreparedStatement(_context, _connection, _cal, method, args);
+
+            if (logger.isInfoEnabled()) {
+                logger.info("PreparedStatement: "
+                            + sqlStatement.createPreparedStatementString(_context, _connection,  method, args));
+            }
+
+            //
+            // special processing for batch updates
+            //
+            if (sqlStatement.isBatchUpdate()) {
+                return ps.executeBatch();
+            }
+
+            //
+            // execute the statement
+            //
+            boolean hasResults = ps.execute();
+
+
+            //
+            // callable statement processing
+            //
+            if (sqlStatement.isCallableStatement()) {
+                SQLParameter[] params = (SQLParameter[]) args[0];
+                for (int i = 0; i < params.length; i++) {
+                    if (params[i].dir != SQLParameter.IN) {
+                        params[i].value = ((CallableStatement) ps).getObject(i + 1);
+                    }
+                }
+                return null;
+            }
+
+
+            //
+            // process returned data
+            //
+            ResultSet rs = null;
+            int updateCount = ps.getUpdateCount();
+
+            if (hasResults) {
+                rs = ps.getResultSet();
+            }
+
+            if (sqlStatement.getsGeneratedKeys()) {
+                rs = ps.getGeneratedKeys();
+                hasResults = true;
+            }
+
+            if (!hasResults && updateCount > -1) {
+                boolean moreResults = ps.getMoreResults();
+                int tempUpdateCount = ps.getUpdateCount();
+
+                while ((moreResults && rs == null) || tempUpdateCount > -1) {
+                    if (moreResults) {
+                        rs = ps.getResultSet();
+                        hasResults = true;
+                        moreResults = false;
+                        tempUpdateCount = -1;
+                    } else {
+                        moreResults = ps.getMoreResults();
+                        tempUpdateCount = ps.getUpdateCount();
+                    }
+                }
+            }
+
+            Object returnObject = null;
+            if (hasResults) {
+
+                //
+                // if a result set mapper was specified in the methods annotation, use it
+                // otherwise find the mapper for the return type in the hashmap
+                //
+                final Class resultSetMapperClass = methodSQL.resultSetMapper();
+                final ResultSetMapper rsm;
+                if (!UndefinedResultSetMapper.class.isAssignableFrom(resultSetMapperClass)) {
+                    if (ResultSetMapper.class.isAssignableFrom(resultSetMapperClass)) {
+                        rsm = (ResultSetMapper) resultSetMapperClass.newInstance();
+                    } else {
+                        throw new ControlException("Result set mappers must be subclasses of ResultSetMapper.class!");
+                    }
+                } else {
+                    if (_resultMappers.containsKey(returnType)) {
+                        rsm = _resultMappers.get(returnType);
+                    } else {
+                        if (_xmlObjectClass != null && _xmlObjectClass.isAssignableFrom(returnType)) {
+                            rsm = _resultMappers.get(_xmlObjectClass);
+                        } else {
+                            rsm = DEFAULT_MAPPER;
+                        }
+                    }
+                }
+
+                returnObject = rsm.mapToResultType(_context, method, rs, _cal);
+                if (rsm.canCloseResultSet() == false) {
+                    _resources.add(ps);
+                }
+
+                //
+                // empty ResultSet
+                //
+            } else {
+                if (returnType.equals(Void.TYPE)) {
+                    returnObject = null;
+                } else if (returnType.equals(Integer.TYPE)) {
+                    returnObject = new Integer(updateCount);
+                } else if (!sqlStatement.isCallableStatement()) {
+                    throw new ControlException("Method " + method.getName() + "is DML but does not return void or int");
+                }
+            }
+            return returnObject;
+
+        } finally {
+            // Keep statements open that have in-use result sets
+            if (ps != null && !_resources.contains(ps)) {
+                ps.close();
+            }
+        }
+    }
+
+// /////////////////////////////////////////// Private Methods ////////////////////////////////////////////
+
+    /**
+     * Get a connection from a DataSource.
+     *
+     * @param jndiName    Specifed in the subclasse's ConnectionDataSource annotation
+     * @param jndiFactory Specified in the subclasse's ConnectionDataSource Annotation.
+     * @return null if a connection cannot be established
+     * @throws SQLException
+     */
+    private Connection getConnectionFromDataSource(String jndiName,
+                                                   Class<? extends JdbcControl.JndiContextFactory> jndiFactory)
+            throws SQLException
+    {
+
+        Connection con = null;
+        try {
+            JndiContextFactory jf = (JndiContextFactory) jndiFactory.newInstance();
+            Context jndiContext = jf.getContext();
+            _dataSource = (DataSource) jndiContext.lookup(jndiName);
+            con = _dataSource.getConnection();
+        } catch (IllegalAccessException iae) {
+            throw new ControlException("IllegalAccessException:", iae);
+        } catch (InstantiationException ie) {
+            throw new ControlException("InstantiationException:", ie);
+        } catch (NamingException ne) {
+            throw new ControlException("NamingException:", ne);
+        }
+        return con;
+    }
+
+    /**
+     * Get a JDBC connection from the DriverManager.
+     *
+     * @param dbDriverClassName Specified in the subclasse's ConnectionDriver annotation.
+     * @param dbUrlStr          Specified in the subclasse's ConnectionDriver annotation.
+     * @param userName          Specified in the subclasse's ConnectionDriver annotation.
+     * @param password          Specified in the subclasse's ConnectionDriver annotation.
+     * @return null if a connection cannot be established.
+     * @throws SQLException
+     */
+    private Connection getConnectionFromDriverManager(String dbDriverClassName, String dbUrlStr,
+                                                      String userName, String password, String propertiesString)
+            throws SQLException
+    {
+
+        Connection con = null;
+        try {
+            Class.forName(dbDriverClassName);
+            if (!EMPTY_STRING.equals(userName)) {
+                con = DriverManager.getConnection(dbUrlStr, userName, password);
+            } else if (!EMPTY_STRING.equals(propertiesString)) {
+                Properties props = parseProperties(propertiesString);
+                if (props == null) {
+                    throw new ControlException("Invalid properties annotation value: " + propertiesString);
+                }
+                con = DriverManager.getConnection(dbUrlStr, props);
+            } else {
+                con = DriverManager.getConnection(dbUrlStr);
+            }
+        } catch (ClassNotFoundException e) {
+            throw new ControlException("Database driver class not found!", e);
+        }
+        return con;
+    }
+
+    /**
+     * Parse the propertiesString into a Properties object.  The string must have the format of:
+     * propertyName=propertyValue;propertyName=propertyValue;...
+     *
+     * @param propertiesString
+     * @return A Properties instance or null if parse fails
+     */
+    private Properties parseProperties(String propertiesString) {
+        Properties properties = null;
+        String[] propPairs = propertiesString.split(";");
+        if (propPairs.length > 0) {
+            properties = new Properties();
+            for (String propPair : propPairs) {
+                int eq = propPair.indexOf('=');
+                assert eq > -1 : "Invalid properties syntax: " + propertiesString;
+                properties.put(propPair.substring(0, eq), propPair.substring(eq + 1, propPair.length()));
+            }
+        }
+        return properties;
+    }
+
+    /**
+     * Set any custom type mappers specifed in the annotation for the connection.  Used for mapping SQL UDTs to
+     * java classes.
+     *
+     * @param typeMappers An array of TypeMapper.
+     */
+    private void setTypeMappers(TypeMapper[] typeMappers) throws SQLException {
+
+        if (typeMappers.length > 0) {
+            Map<String, Class<?>> mappers = _connection.getTypeMap();
+            for (TypeMapper t : typeMappers) {
+                mappers.put(t.UDTName(), t.mapperClass());
+            }
+            _connection.setTypeMap(mappers);
+        }
+    }
+}

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/ResultSetHashMap.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/ResultSetHashMap.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/ResultSetHashMap.java (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/ResultSetHashMap.java Mon May  9 13:17:58 2005
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * 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.
+ *
+ * $Header:$
+ */
+
+package org.apache.beehive.controls.system.jdbc;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.HashMap;
+
+/**
+ * The ResultSetHashMap class extends a standard HashMap and
+ * populates it with data derived from a JDBC ResultSet.
+ * <p/>
+ * Note: the keys are treated case-insensitively, and therefore requests
+ * made on the map are case-insensitive.  Any direct access to the keys
+ * will yield uppercase keys.
+ * <p/>
+ * Note: only the row associated with the current cursor position
+ * is used.
+ */
+public class ResultSetHashMap extends HashMap<String, Object> {
+
+    ResultSetHashMap() {
+        super();
+    }
+
+    ResultSetHashMap(int size) {
+        super(size);
+    }
+
+    /**
+     * This constructor is optimized for being called in a loop.
+     * Preserve the upper case column list for performance.
+     */
+    ResultSetHashMap(ResultSet rs, String[] keys) throws SQLException {
+        super();
+        assert keys.length == rs.getMetaData().getColumnCount() + 1;
+
+        for (int i = 1; i < keys.length; i++) {
+            assert keys[i].equals(keys[i].toUpperCase());
+            super.put(keys[i], rs.getObject(i));
+        }
+    }
+
+
+    ResultSetHashMap(ResultSet rs) throws SQLException {
+        super();
+        ResultSetMetaData md = rs.getMetaData();
+        for (int i = 1; i <= md.getColumnCount(); i++) {
+            super.put(md.getColumnName(i).toUpperCase(), rs.getObject(i));
+        }
+    }
+
+
+    public boolean containsKey(String key) {
+        return super.containsKey(key.toUpperCase());
+    }
+
+
+    public Object get(String key) {
+        return super.get(key.toUpperCase());
+    }
+
+
+    public Object put(String key, Object value) {
+        return super.put(key.toUpperCase(), value);
+    }
+
+
+    public Object remove(String key) {
+        return super.remove(key.toUpperCase());
+    }
+}
+

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/ResultSetIterator.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/ResultSetIterator.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/ResultSetIterator.java (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/ResultSetIterator.java Mon May  9 13:17:58 2005
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * 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.
+ *
+ * $Header:$
+ */
+package org.apache.beehive.controls.system.jdbc;
+
+import org.apache.beehive.controls.api.ControlException;
+import org.apache.beehive.controls.api.context.ControlBeanContext;
+
+import java.lang.reflect.Method;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Calendar;
+import java.util.NoSuchElementException;
+
+/**
+ * Used by DefaultIteratorResultSetMapper for mapping a ResultSet to an Iterator type.
+ */
+public class ResultSetIterator implements java.util.Iterator {
+
+    private final Class _returnClass;
+    private final ResultSet _rs;
+    private final RowMapper _rowMapper;
+
+    private boolean _primed = false;
+
+    /**
+     * Create a new ResultSetIterator.
+     * @param context A ControlBeanContext.
+     * @param method The annotated method.
+     * @param rs The ResultSet to map.
+     * @param cal A Calendar instance for mapping date/time values.
+     */
+    ResultSetIterator(ControlBeanContext context, Method method, ResultSet rs, Calendar cal) {
+        _rs = rs;
+
+        JdbcControl.SQL methodSQL = (JdbcControl.SQL) context.getMethodPropertySet(method, JdbcControl.SQL.class);
+        _returnClass = methodSQL.iteratorElementType();
+
+        if (_returnClass == null) {
+            throw new ControlException("Invalid return class declared for Iterator:" + _returnClass.getName());
+        }
+
+        _rowMapper = RowMapperFactory.getRowMapper(rs, _returnClass, cal);
+    }
+
+    /**
+     * Does this iterater have more elements?
+     * @return true if there is another element
+     */
+    public boolean hasNext() {
+        if (_primed) {
+            return true;
+        }
+
+        try {
+            _primed = _rs.next();
+        } catch (SQLException sqle) {
+            return false;
+        }
+        return _primed;
+    }
+
+    /**
+     * Get the next element in the iteration.
+     * @return The next element in the iteration.
+     */
+    public Object next() {
+        try {
+            if (!_primed) {
+                _primed = _rs.next();
+                if (!_primed) {
+                    throw new NoSuchElementException();
+                }
+            }
+            // reset upon consumption
+            _primed = false;
+            return _rowMapper.mapRowToReturnType();
+        } catch (SQLException e) {
+            // Since Iterator interface is locked, all we can do
+            // is put the real exception inside an expected one.
+            NoSuchElementException xNoSuch = new NoSuchElementException("ResultSet exception: " + e);
+            xNoSuch.initCause(e);
+            throw xNoSuch;
+        }
+    }
+
+    /**
+     * Remove is currently not supported.
+     */
+    public void remove() {
+        throw new UnsupportedOperationException("remove not supported");
+    }
+}
+

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/ResultSetMapper.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/ResultSetMapper.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/ResultSetMapper.java (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/ResultSetMapper.java Mon May  9 13:17:58 2005
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * 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.
+ *
+ * $Header:$
+ */
+package org.apache.beehive.controls.system.jdbc;
+
+import org.apache.beehive.controls.api.context.ControlBeanContext;
+
+import java.lang.reflect.Method;
+import java.sql.ResultSet;
+import java.util.Calendar;
+
+/**
+ * Extend this class to create new ResultSet mappers. The extended class will be invoked by the JdbcController
+ * when it is time to map a ResultSet to a method's return type.
+ *
+ * ResultSet mappers are specified on a per method basis using the SQL annotation's resultSetMapper field.
+ */
+public abstract class ResultSetMapper {
+
+    /**
+     * Map a ResultSet to an object type
+     *
+     * @param context   A ControlBeanContext instance, see Beehive controls javadoc for additional information
+     * @param m         Method assoicated with this call.
+     * @param resultSet Result set to map.
+     * @param cal       A Calendar instance for time/date value resolution.
+     * @return          The Object resulting from the ResultSet
+     */
+    public abstract Object mapToResultType(ControlBeanContext context, Method m, ResultSet resultSet, Calendar cal);
+
+    /**
+     * Can the ResultSet which this mapper uses be closed by the Jdbc control?
+     * @return true if the ResultSet can be closed by the JdbcControl
+     */
+    public boolean canCloseResultSet() { return true; }
+}

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowMapper.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowMapper.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowMapper.java (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowMapper.java Mon May  9 13:17:58 2005
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * 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.
+ *
+ * $Header:$
+ */
+
+package org.apache.beehive.controls.system.jdbc;
+
+import org.apache.beehive.controls.api.ControlException;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.Calendar;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * Abstract base class for all row mappers.
+ *
+ * RowMappers are used to map the contents of a row in a ResultSet to the return type of an annotated method.
+ * Supported RowMapper types include: HashMap, Map, Object, XmlObject.  When a ResultSetMapper is ready to
+ * map a ResultSet row to an object, it requests a RowMapper for the return type of the method from the
+ * RowMapperFactory.
+ *
+ */
+public abstract class RowMapper {
+
+    private static final String SETTER_NAME_REGEX = "^(set)([A-Z_]\\w*+)";
+    protected static final TypeMappingsFactory _tmf = TypeMappingsFactory.getInstance();
+    protected static final Pattern _setterRegex = Pattern.compile(SETTER_NAME_REGEX);
+
+    /** ResultSet to map. */
+    protected final ResultSet _resultSet;
+
+    /** Calendar instance for date/time mappings. */
+    protected final Calendar _cal;
+
+    /** Class to map ResultSet Rows to. */
+    protected final Class<?> _returnTypeClass;
+
+    /**
+     * Create a new RowMapper for the specified ResultSet and return type Class.
+     * @param resultSet ResultSet to map
+     * @param returnTypeClass Class to map ResultSet rows to.
+     * @param cal Calendar instance for date/time values.
+     */
+    protected RowMapper(ResultSet resultSet, Class<?> returnTypeClass, Calendar cal) {
+        _resultSet = resultSet;
+        _returnTypeClass = returnTypeClass;
+        _cal = cal;
+    }
+
+    /**
+     * Map a ResultSet row to the return type class
+     * @return An instance of class.
+     */
+    public abstract Object mapRowToReturnType();
+
+
+    /**
+     * Build a String array of column names from the ResultSet.
+     * @return A String array containing the column names contained within the ResultSet.
+     * @throws SQLException on error
+     */
+    protected String[] getKeysFromResultSet() throws SQLException {
+
+        String[] keys;
+        final ResultSetMetaData md = _resultSet.getMetaData();
+        final int columnCount = md.getColumnCount();
+
+        keys = new String[columnCount + 1];
+        for (int i = 1; i <= columnCount; i++) {
+            keys[i] = md.getColumnName(i).toUpperCase();
+        }
+        return keys;
+    }
+
+    /**
+     * Determine if the given method is a java bean setter method.
+     * @param method Method to check
+     * @return True if the method is a setter method.
+     */ 
+    protected boolean isSetterMethod(Method method) {
+        Matcher matcher = _setterRegex.matcher(method.getName());
+        if (matcher.matches()) {
+
+            if (Modifier.isStatic(method.getModifiers())) return false;
+            if (!Modifier.isPublic(method.getModifiers())) return false;
+            if (!Void.TYPE.equals(method.getReturnType())) return false;
+
+            // method parameter checks
+            Class[] params = method.getParameterTypes();
+            if (params.length != 1) return false;
+            if (TypeMappingsFactory.TYPE_UNKNOWN == _tmf.getTypeId(params[0])) return false;
+
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Extract a column value from the ResultSet and return it as resultType.
+     *
+     * @param index The column index of the value to extract from the ResultSet.
+     * @param resultType The return type. Defined in TypeMappingsFactory.
+     * @return The extracted value
+     * @throws java.sql.SQLException on error.
+     */
+    protected Object extractColumnValue(int index, int resultType) throws SQLException {
+
+        switch (resultType) {
+            case TypeMappingsFactory.TYPE_INT:
+                return new Integer(_resultSet.getInt(index));
+            case TypeMappingsFactory.TYPE_LONG:
+                return new Long(_resultSet.getLong(index));
+            case TypeMappingsFactory.TYPE_FLOAT:
+                return new Float(_resultSet.getFloat(index));
+            case TypeMappingsFactory.TYPE_DOUBLE:
+                return new Double(_resultSet.getDouble(index));
+            case TypeMappingsFactory.TYPE_BYTE:
+                return new Byte(_resultSet.getByte(index));
+            case TypeMappingsFactory.TYPE_SHORT:
+                return new Short(_resultSet.getShort(index));
+            case TypeMappingsFactory.TYPE_BOOLEAN:
+                return _resultSet.getBoolean(index) ? Boolean.TRUE : Boolean.FALSE;
+            case TypeMappingsFactory.TYPE_INT_OBJ:
+                {
+                    int i = _resultSet.getInt(index);
+                    return _resultSet.wasNull() ? null : new Integer(i);
+                }
+            case TypeMappingsFactory.TYPE_LONG_OBJ:
+                {
+                    long i = _resultSet.getLong(index);
+                    return _resultSet.wasNull() ? null : new Long(i);
+                }
+            case TypeMappingsFactory.TYPE_FLOAT_OBJ:
+                {
+                    float i = _resultSet.getFloat(index);
+                    return _resultSet.wasNull() ? null : new Float(i);
+                }
+            case TypeMappingsFactory.TYPE_DOUBLE_OBJ:
+                {
+                    double i = _resultSet.getDouble(index);
+                    return _resultSet.wasNull() ? null : new Double(i);
+                }
+            case TypeMappingsFactory.TYPE_BYTE_OBJ:
+                {
+                    byte i = _resultSet.getByte(index);
+                    return _resultSet.wasNull() ? null : new Byte(i);
+                }
+            case TypeMappingsFactory.TYPE_SHORT_OBJ:
+                {
+                    short i = _resultSet.getShort(index);
+                    return _resultSet.wasNull() ? null : new Short(i);
+                }
+            case TypeMappingsFactory.TYPE_BOOLEAN_OBJ:
+                {
+                    boolean i = _resultSet.getBoolean(index);
+                    return _resultSet.wasNull() ? null : (i ? Boolean.TRUE : Boolean.FALSE);
+                }
+            case TypeMappingsFactory.TYPE_STRING:
+            case TypeMappingsFactory.TYPE_XMLBEAN_ENUM:
+                return _resultSet.getString(index);
+            case TypeMappingsFactory.TYPE_BIG_DECIMAL:
+                return _resultSet.getBigDecimal(index);
+            case TypeMappingsFactory.TYPE_BYTES:
+                return _resultSet.getBytes(index);
+            case TypeMappingsFactory.TYPE_TIMESTAMP:
+                {
+                    if (null == _cal)
+                        return _resultSet.getTimestamp(index);
+                    else
+                        return _resultSet.getTimestamp(index, _cal);
+                }
+            case TypeMappingsFactory.TYPE_TIME:
+                {
+                    if (null == _cal)
+                        return _resultSet.getTime(index);
+                    else
+                        return _resultSet.getTime(index, _cal);
+                }
+            case TypeMappingsFactory.TYPE_SQLDATE:
+                {
+                    if (null == _cal)
+                        return _resultSet.getDate(index);
+                    else
+                        return _resultSet.getDate(index, _cal);
+                }
+            case TypeMappingsFactory.TYPE_DATE:
+                {
+                    // convert explicity to java.util.Date
+                    // 12918 |  knex does not return java.sql.Date properly from web service
+                    java.sql.Timestamp ts = (null == _cal) ? _resultSet.getTimestamp(index) : _resultSet.getTimestamp(index, _cal);
+                    if (null == ts)
+                        return null;
+                    return new java.util.Date(ts.getTime());
+                }
+            case TypeMappingsFactory.TYPE_CALENDAR:
+                {
+                    java.sql.Timestamp ts = (null == _cal) ? _resultSet.getTimestamp(index) : _resultSet.getTimestamp(index, _cal);
+                    if (null == ts)
+                        return null;
+                    Calendar c = (null == _cal) ? Calendar.getInstance() : (Calendar) _cal.clone();
+                    c.setTimeInMillis(ts.getTime());
+                    return c;
+                }
+            case TypeMappingsFactory.TYPE_REF:
+                return _resultSet.getRef(index);
+            case TypeMappingsFactory.TYPE_BLOB:
+                return _resultSet.getBlob(index);
+            case TypeMappingsFactory.TYPE_CLOB:
+                return _resultSet.getClob(index);
+            case TypeMappingsFactory.TYPE_ARRAY:
+                return _resultSet.getArray(index);
+            case TypeMappingsFactory.TYPE_READER:
+            case TypeMappingsFactory.TYPE_STREAM:
+                throw new ControlException("streaming return types are not supported by the JdbcControl; use ResultSet instead");
+            case TypeMappingsFactory.TYPE_STRUCT:
+            case TypeMappingsFactory.TYPE_UNKNOWN:
+                // JAVA_TYPE (could be any), or REF
+                return _resultSet.getObject(index);
+            default:
+                throw new ControlException("internal error: unknown type ID: " + Integer.toString(resultType));
+        }
+    }
+}

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowMapperFactory.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowMapperFactory.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowMapperFactory.java (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowMapperFactory.java Mon May  9 13:17:58 2005
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * 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.
+ *
+ * $Header:$
+ */
+
+package org.apache.beehive.controls.system.jdbc;
+
+import org.apache.beehive.controls.api.ControlException;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.sql.ResultSet;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Factory for creating row mappers.
+ * <p/>
+ * Row mapper types supported by this factory include: HashMap, Map, Object, XmlObject. The factory determines the
+ * proper row mapper to use by checking its List of RowMappers against the type of mapping requested.  When performing
+ * the lookup, the factory attempts to find the most specific type match.  If a match can't be found the most general
+ * type of RowMapper is returned, RowToObjectMapper.
+ */
+public final class RowMapperFactory {
+
+    private static final HashMap<Class, Class<? extends RowMapper>> _rowMappings
+            = new HashMap<Class, Class<? extends RowMapper>>();
+
+    private static Class<? extends RowMapper> DEFAULT_OBJ_ROWMAPPING = RowToObjectMapper.class;
+
+    private static Class XMLOBJ_CLASS = null;
+    private static Class<? extends RowMapper> DEFAULT_XMLOBJ_ROWMAPPING = null;
+    private final static Class[] _params = {ResultSet.class, Class.class, Calendar.class};
+
+    static {
+
+        _rowMappings.put(HashMap.class, RowToHashMapMapper.class);
+        _rowMappings.put(Map.class, RowToMapMapper.class);
+
+        try {
+            XMLOBJ_CLASS = Class.forName("org.apache.xmlbeans.XmlObject");
+            DEFAULT_XMLOBJ_ROWMAPPING = RowToXmlObjectMapper.class;
+        } catch (ClassNotFoundException e) {
+            // NOOP if apache xml beans not present
+        }
+    }
+
+    /**
+     * Get a RowMapper instance which knows how to map a ResultSet row to the given return type.
+     *
+     * @param rs              The ResultSet to map.
+     * @param returnTypeClass The class to map a ResultSet row to.
+     * @param cal             Calendar instance for mapping date/time values.
+     * @return A RowMapper instance.
+     */
+    public static RowMapper getRowMapper(ResultSet rs, Class returnTypeClass, Calendar cal) {
+
+        Class<? extends RowMapper> rm = _rowMappings.get(returnTypeClass);
+        if (rm != null) {
+            return getMapper(rm, rs, returnTypeClass, cal);
+        }
+
+        //
+        // if we made it to here, check if the default XMLObject Mapper can be used,
+        // otherwise use the default object mapper
+        //
+        if (XMLOBJ_CLASS != null && XMLOBJ_CLASS.isAssignableFrom(returnTypeClass)) {
+            return getMapper(DEFAULT_XMLOBJ_ROWMAPPING, rs, returnTypeClass, cal);
+        } else {
+            return getMapper(DEFAULT_OBJ_ROWMAPPING, rs, returnTypeClass, cal);
+        }
+    }
+
+    /**
+     * Add a new row mapper to the list of available row mappers.  The getRowMapper method traverses the
+     * list of mappers from beginning to end, checking to see if a mapper can handle the specified
+     * returnTypeClass.  There is a default mapper which is used if a match cannot be found in the list.
+     *
+     * @param returnTypeClass Class which this mapper maps a row to.
+     * @param rowMapperClass  The row mapper class.
+     */
+    public static void addRowMapping(Class returnTypeClass, Class<? extends RowMapper> rowMapperClass) {
+        _rowMappings.put(returnTypeClass, rowMapperClass);
+    }
+
+    /**
+     * Replace a row mapping.
+     *
+     * @param returnTypeClass Class which this mapper maps a row to.
+     * @param rowMapperClass  The row mapper class.
+     * @return if the mapper was replaced, false mapper for returnTypeClass was not found, no action taken.
+     */
+    public static Class<? extends RowMapper> replaceRowMapping(Class returnTypeClass, Class<? extends RowMapper> rowMapperClass) {
+        return _rowMappings.put(returnTypeClass, rowMapperClass);
+    }
+
+    /**
+     * remove the  row mapping for the specified class type.
+     *
+     * @param returnTypeClass
+     * @return the RowMapper class which was removed, null if returnTypeClass did not match any of the registered
+     *         row mappers.
+     */
+    public static Class<? extends RowMapper> removeRowMapping(Class returnTypeClass) {
+        return _rowMappings.remove(returnTypeClass);
+    }
+
+    /**
+     * Sets the rowmapper for Object.class
+     *
+     * @param rowMapperClass
+     */
+    public static Class <? extends RowMapper> setDefaultRowMapping(Class<? extends RowMapper> rowMapperClass) {
+        Class<? extends RowMapper> ret = DEFAULT_OBJ_ROWMAPPING;
+        DEFAULT_OBJ_ROWMAPPING = rowMapperClass;
+        return ret;
+    }
+
+    /**
+     * Sets the rowmapper for XmlObject.class
+     *
+     * @param rowMapperClass
+     */
+    public static Class<? extends RowMapper> setDefaultXmlRowMapping(Class mapToClass, Class<? extends RowMapper> rowMapperClass) {
+        Class<? extends RowMapper> ret = DEFAULT_XMLOBJ_ROWMAPPING;
+        DEFAULT_XMLOBJ_ROWMAPPING = rowMapperClass;
+        XMLOBJ_CLASS = mapToClass;
+        return ret;
+    }
+
+    /**
+     * Create an instance of the RowMapper class.
+     *
+     * @param rowMapper
+     * @param rs         ResultSet we are mapping from.
+     * @param returnType Class to map rows to.
+     * @param cal        Calendar instance for date/time values.
+     * @return A RowMapper instance.
+     */
+    private static RowMapper getMapper(Class<? extends RowMapper> rowMapper, ResultSet rs, Class returnType, Calendar cal)
+    {
+        Constructor c = null;
+        try {
+            c = rowMapper.getDeclaredConstructor(_params);
+            return (RowMapper) c.newInstance(new Object[]{rs, returnType, cal});
+        } catch (NoSuchMethodException e) {
+            throw new ControlException("Failure creating new instance of RowMapper, " + e.toString(), e);
+        } catch (InstantiationException e) {
+            throw new ControlException("Failure creating new instance of RowMapper, " + e.toString(), e);
+        } catch (IllegalAccessException e) {
+            throw new ControlException("Failure creating new instance of RowMapper, " + e.toString(), e);
+        } catch (InvocationTargetException e) {
+            throw new ControlException("Failure creating new instance of RowMapper, " + e.getCause().toString(), e);
+        }
+    }
+}

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowToHashMapMapper.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowToHashMapMapper.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowToHashMapMapper.java (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowToHashMapMapper.java Mon May  9 13:17:58 2005
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * 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.
+ *
+ * $Header:$
+ */
+
+package org.apache.beehive.controls.system.jdbc;
+
+import org.apache.beehive.controls.api.ControlException;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Calendar;
+
+/**
+ * Map a ResultSet row to a java.util.HashMap object
+ */
+public final class RowToHashMapMapper extends RowMapper {
+
+    private final String[] _keys;
+
+
+    /**
+     * Create a new RowToHashMapMapper.
+     * @param resultSet ResultSet to map
+     * @param returnTypeClass Class to map to.
+     * @param cal Calendar instance for date/time mappings.
+     */
+    RowToHashMapMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal) {
+        super(resultSet, returnTypeClass, cal);
+        try {
+            _keys = getKeysFromResultSet();
+        } catch (SQLException sql) {
+            throw new ControlException("RowToHashMapMapper: SQLException: " + sql.getMessage(), sql);
+        }
+    }
+
+    /**
+     * Do the mapping.
+     * @return A ResultSetHashMap object.
+     * @throws ControlException on error.
+     */
+    public Object mapRowToReturnType() {
+        try {
+            return new ResultSetHashMap(_resultSet, _keys);
+        } catch (SQLException e) {
+            throw new ControlException("Exception creating HashMap return type: ", e);
+        }
+    }
+}

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowToMapMapper.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowToMapMapper.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowToMapMapper.java (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowToMapMapper.java Mon May  9 13:17:58 2005
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * 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.
+ *
+ * $Header:$
+ */
+
+package org.apache.beehive.controls.system.jdbc;
+
+import org.apache.beehive.controls.api.ControlException;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Calendar;
+
+/**
+ * Map a ResultSet row to a java.util.Map object.
+ */
+public final class RowToMapMapper extends RowMapper {
+    
+    private final String[] _keys;
+
+    /**
+     * Create a new RowToMapMapper.
+     * @param resultSet ResultSet to map
+     * @param returnTypeClass Class to map to.
+     * @param cal Calendar instance for date/time mappings.
+     */
+    RowToMapMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal) {
+        super(resultSet, returnTypeClass, cal);
+        try {
+        _keys = getKeysFromResultSet();
+        } catch (SQLException e) {
+            throw new ControlException("RowToMapMapper: SQLException: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Do the mapping.
+     * @return A Map.
+     * @throws ControlException on error.
+     */
+    public Object mapRowToReturnType() {
+        try {
+        return Collections.unmodifiableMap(new ResultSetHashMap(_resultSet, _keys));
+        } catch (SQLException e) {
+           throw new ControlException("Exception while creating ResultSetHashMap.", e);
+        }
+    }
+}

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowToObjectMapper.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowToObjectMapper.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowToObjectMapper.java (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/RowToObjectMapper.java Mon May  9 13:17:58 2005
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * 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.
+ *
+ * $Header:$
+ */
+
+package org.apache.beehive.controls.system.jdbc;
+
+import org.apache.beehive.controls.api.ControlException;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.Calendar;
+import java.util.HashMap;
+
+/**
+ * Map a ResultSet row to an Object. This mapper uses Java reflection to perform the mapping.  The Class being mapped
+ * to must have setter methods which match the ResultSet column names.  For example, if a column in the ResultSet
+ * named USERID, the object must have a setter method named setUserid().  If a setter method cannot be class fields
+ * are also checked, the same naming conventions applies, USERID -> userid.
+ */
+public class RowToObjectMapper extends RowMapper {
+
+    private final int _columnCount;
+
+    private AccessibleObject[] _fields;
+    private int[] _fieldTypes;
+
+    private final Object[] _args = new Object[1];
+
+    /**
+     * Create a new RowToObjectMapper.
+     *
+     * @param resultSet       ResultSet to map
+     * @param returnTypeClass Class to map to.
+     * @param cal             Calendar instance for date/time mappings.
+     */
+    RowToObjectMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal) {
+        super(resultSet, returnTypeClass, cal);
+
+        _fields = null;
+
+        try {
+            _columnCount = resultSet.getMetaData().getColumnCount();
+        } catch (SQLException e) {
+            throw new ControlException("RowToObjectMapper: SQLException: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Do the mapping.
+     *
+     * @return An object instance.
+     */
+    public Object mapRowToReturnType() {
+
+        Object resultObject = null;
+
+        // if the ResultSet only contains a single column we may be able to map directly
+        // to the return type -- if so we don't need to build any structures to support
+        // mapping
+        if (_columnCount == 1) {
+
+            final int typeId = _tmf.getTypeId(_returnTypeClass);
+
+            try {
+                if (typeId != TypeMappingsFactory.TYPE_UNKNOWN) {
+                    return extractColumnValue(1, typeId);
+                } else {
+                    // we still might want a single value (i.e. java.util.Date)
+                    Object val = extractColumnValue(1, typeId);
+                    if (_returnTypeClass.isAssignableFrom(val.getClass())) {
+                        return val;
+                    }
+                }
+            } catch (SQLException e) {
+                throw new ControlException(e.getMessage(), e);
+            }
+        }
+
+        if (_fields == null) {
+            try {
+                getFieldMappings();
+            } catch (SQLException e) {
+                throw new ControlException(e.getMessage(), e);
+            }
+        }
+
+        try {
+            resultObject = _returnTypeClass.newInstance();
+        } catch (InstantiationException e) {
+            throw new ControlException("InstantiationException when trying to create instance of : "
+                                       + _returnTypeClass.getName(), e);
+        } catch (IllegalAccessException e) {
+            throw new ControlException("IllegalAccessException when trying to create instance of : "
+                                       + _returnTypeClass.getName(), e);
+        }
+
+        for (int i = 1; i < _fields.length; i++) {
+            AccessibleObject f = _fields[i];
+            Object resultValue = null;
+
+            try {
+                resultValue = extractColumnValue(i, _fieldTypes[i]);
+                if (f instanceof Field) {
+                    ((Field) f).set(resultObject, resultValue);
+                } else {
+                    _args[0] = resultValue;
+                    ((Method) f).invoke(resultObject, _args);
+                }
+            } catch (SQLException e) {
+                throw new ControlException(e.getMessage(), e);
+            } catch (IllegalArgumentException iae) {
+
+                try {
+                    ResultSetMetaData md = _resultSet.getMetaData();
+                    if (f instanceof Field) {
+                        throw new ControlException("The declared Java type for field " + ((Field) f).getName()
+                                                   + ((Field) f).getType().toString()
+                                                   + " is incompatible with the SQL format of column " + md.getColumnName(i).toString()
+                                                   + md.getColumnTypeName(i).toString()
+                                                   + " which returns objects of type " + resultValue.getClass().getName());
+                    } else {
+                        throw new ControlException("The declared Java type for method " + ((Method) f).getName()
+                                                   + ((Method) f).getParameterTypes()[0].toString()
+                                                   + " is incompatible with the SQL format of column " + md.getColumnName(i).toString()
+                                                   + md.getColumnTypeName(i).toString()
+                                                   + " which returns objects of type " + resultValue.getClass().getName());
+                    }
+                } catch (SQLException e) {
+                    throw new ControlException(e.getMessage(), e);
+                }
+
+            } catch (IllegalAccessException e) {
+                if (f instanceof Field) {
+                    throw new ControlException("IllegalAccessException when trying to access field " + ((Field) f).getName(), e);
+                } else {
+                    throw new ControlException("IllegalAccessException when trying to access method " + ((Method) f).getName(), e);
+                }
+            } catch (InvocationTargetException e) {
+                if (f instanceof Field) {
+                    throw new ControlException("InvocationTargetException when trying to access field " + ((Field) f).getName(), e);
+                } else {
+                    throw new ControlException("InvocationTargetException when trying to access method " + ((Method) f).getName(), e);
+                }
+            }
+        }
+        return resultObject;
+    }
+
+    /**
+     * Build the structures necessary to do the mapping
+     *
+     * @throws SQLException on error.
+     */
+    protected void getFieldMappings()
+            throws SQLException {
+
+        final String[] keys = getKeysFromResultSet();
+
+        //
+        // find fields or setters for return class
+        //
+        HashMap<String, AccessibleObject> mapFields = new HashMap<String, AccessibleObject>(_columnCount * 2);
+        for (int i = 1; i <= _columnCount; i++) {
+            mapFields.put(keys[i], null);
+        }
+
+        // public methods
+        Method[] classMethods = _returnTypeClass.getMethods();
+        for (Method m : classMethods) {
+
+            if (isSetterMethod(m)) {
+                final String fieldName = m.getName().substring(3).toUpperCase();
+                if (mapFields.containsKey(fieldName)) {
+
+                    // check for overloads
+                    Object field = mapFields.get(fieldName);
+                    if (field == null) {
+                        mapFields.put(fieldName, m);
+                    } else {
+                        throw new ControlException("Unable to choose between overloaded methods " + m.getName()
+                                                   + " on the " + _returnTypeClass.getName() + " class. Mapping is done using "
+                                                   + "a case insensitive comparision of SQL ResultSet columns to field "
+                                                   + "names and public setter methods on the return class.");
+                    }
+                }
+            }
+        }
+
+        // fix for 8813: include inherited and non-public fields
+        for (Class clazz = _returnTypeClass; clazz != null && clazz != Object.class; clazz = clazz.getSuperclass()) {
+            Field[] classFields = clazz.getDeclaredFields();
+            for (Field f : classFields) {
+                if (Modifier.isStatic(f.getModifiers())) continue;
+                if (!Modifier.isPublic(f.getModifiers())) continue;
+                String fieldName = f.getName().toUpperCase();
+                if (!mapFields.containsKey(fieldName)) continue;
+
+                Object field = mapFields.get(fieldName);
+                if (field == null) {
+                    mapFields.put(fieldName, f);
+                }
+            }
+        }
+
+        // finally actually init the fields array
+        _fields = new AccessibleObject[_columnCount + 1];
+        _fieldTypes = new int[_columnCount + 1];
+
+        for (int i = 1; i < _fields.length; i++) {
+            AccessibleObject f = mapFields.get(keys[i]);
+            if (f == null) {
+                throw new ControlException("Unable to map the SQL column " + keys[i]
+                                           + " to a field on the " + _returnTypeClass.getName() +
+                                           " class. Mapping is done using a case insensitive comparision of SQL ResultSet "
+                                           + "columns to field names and public setter methods on the return class.");
+            }
+
+            _fields[i] = f;
+            if (f instanceof Field) {
+                _fieldTypes[i] = _tmf.getTypeId(((Field) f).getType());
+            } else {
+                _fieldTypes[i] = _tmf.getTypeId(((Method) f).getParameterTypes()[0]);
+            }
+        }
+    }
+}