text: var _ = require('underscore');
  var java = require('java');
  java.options.push('-Xrs');
  
  function trim1 (str) {
    return (str || '').replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  }
  
  function JDBCConn() {
    this._config = {};
    this._conn = null;
  }
  
  JDBCConn.prototype.initialize = function(config, callback) {
    var self = this;
    self._config = config;
  
    if (self._config.libpath) {
      java.classpath.push(self._config.libpath);
    }
    if (self._config.libs) {
     java.classpath.push.apply(java.classpath, self._config.libs);
    }
  
    java.newInstance(self._config.drivername, function(err, driver) {
      if (err) {
        return callback(err);
      } else {
        java.callStaticMethod('java.sql.DriverManager','registerDriver', driver, function(err, result) {
          if (err) {
            return callback(err);
          } else {
            return callback(null, self._config.drivername);
          }
        });
      }
    });
  };
  
  JDBCConn.prototype.open = function(callback) {
    var self = this;
  
    if(self._config.user || self._config.password) {
      java.callStaticMethod('java.sql.DriverManager', 'getConnection', self._config.url, self._config.user, self._config.password, function (err, conn) {
        if (err) {
          return callback(err);
        } else {
          self._conn = conn;
          return callback(null, conn);
        }
      });
    } else {
      java.callStaticMethod('java.sql.DriverManager', 'getConnection', self._config.url, function (err, conn) {
        if (err) {
          return callback(err);
        } else {
          self._conn = conn;
          return callback(null, conn);
        }
      });
    }
  };
  
  JDBCConn.prototype.close = function(callback) {
    var self = this;
  
    if (self._conn) {
      self._conn.close(function(err) {
        if (err) {
          return callback(err);
        } else {
          self._conn = null;
          return callback(null);
        }
      });
    }
  };
  
  JDBCConn.prototype.commit = function(callback) {
    var self = this;
  
    if (self._conn) {
      self._conn.commit(function(err) {
        if (err) {
          return callback(err);
        } else {
          return callback(null);
        }
      });
    }
  };
  
  JDBCConn.prototype.executeQuery = function(sql, callback) {
    var self = this;
  
    self._conn.createStatement(function(err, statement) {
      if (err) {
        return callback(err);
      } else {
        statement.executeQuery(sql ,function(err,resultset) {
          if (err) {
            return callback(err);
          } else if (resultset) {
            resultset.getMetaData(function(err,rsmd) {
              if (err) {
                return callback(err);
              } else {
                var results = [];
                var cc = rsmd.getColumnCountSync();
                var columns = [''];
                var types = [''];
                for(var i = 1; i <= cc; i++) {
                  var colType = rsmd.getColumnTypeNameSync(i);
                  types.push(colType);
  
                  var colname = rsmd.getColumnNameSync(i);
                  columns.push(colname);
                }
                var next = resultset.nextSync();
                var processRow = function(next){
                  if(next){
                    setImmediate(function(){
                      var row = {};
                      for(var a = 1; a <= cc; a++) {
                        if (types[a] == "SYS.XMLTYPE") {
                          var sqlXML = resultset.getSQLXMLSync(a);
                          if (sqlXML) {
                            row[columns[a]] = {"value": trim1(sqlXML.getStringSync()), "type": types[a]};
                          }
                          else {
                            row[columns[a]] = {"value": "", "type": types[a], "is_null": true};
                          }
                        }
                        else if (types[a] == "CLOB" || types[a] == "BLOB")  {
                          row[columns[a]] = {"value": '', "type": types[a]};
                        }
                        else {
                          row[columns[a]] = {"value": trim1(resultset.getStringSync(a)), "type": types[a], "is_null": (resultset.getStringSync(a) == null)};
                        }
                      }
                      results.push(row);
                      next = resultset.nextSync();
                      processRow(next);
                    });
                  } else {
                    callback(null, results);
                  }
                };
                processRow(next);
              }
            });
          } else {
            return callback(null, null);
          }
        });
      }
    });
  };
  
  
  JDBCConn.prototype.getTableMetaData = function(tableName, callback) {
    var self = this;
  
    self._conn.createStatement(function(err, statement) {
      if (err) {
        return callback(err);
      } else {
        statement.executeQuery("SELECT * FROM " + tableName + " WHERE rownum = 1" ,function(err,resultset) {
          if (err) {
            return callback(err);
          } else if (resultset) {
            resultset.getMetaData(function(err,rsmd) {
              if (err) {
                return callback(err);
              } else {
                var results = {};
                results["table_name"] = tableName;
                results["column_count"] = rsmd.getColumnCountSync();
                results["columns"] = [];
                for(var i = 1; i <= results["column_count"]; i++) {
                  var colMetaData = {};
                  colMetaData.name = rsmd.getColumnNameSync(i);
                  colMetaData.type = rsmd.getColumnTypeNameSync(i);
                  colMetaData.precision = rsmd.getPrecisionSync(i);
                  colMetaData.scale = rsmd.getScaleSync(i);
                  colMetaData.auto_increment = rsmd.isAutoIncrementSync(i);
                  colMetaData.case_sensitive = rsmd.isCaseSensitiveSync(i);
                  colMetaData.currency = rsmd.isCurrencySync(i);
                  colMetaData.nullable = rsmd.isNullableSync(i);
                  colMetaData.read_only = rsmd.isReadOnlySync(i);
                  colMetaData.searchable = rsmd.isSearchableSync(i);
                  colMetaData.signed = rsmd.isSignedSync(i);
                  colMetaData.writable = rsmd.isWritableSync(i);
                  results["columns"].push(colMetaData);
                }
                callback(null, results);
              }
            });
          } else {
            return callback(null, null);
          }
        });
      }
    });
  };
  
  JDBCConn.prototype.executeUpdate = function(sql, callback) {
    var self = this;
  
    self._conn.createStatement(function(err, statement) {
      if (err) {
        return callback(err);
      } else {
        statement.executeUpdate(sql, function(err, rowcount) {
          if (err) {
            return callback(err);
          } else {
            return callback(null, rowcount);
          }
        });
      }
    });
  };
  
  var TYPES = {"VARCHAR": 12
              , "INTEGER": 4
              , "SQLXML": 2009
              , "DATE": 91}
  /**
  binds format
  {
    ":bind1" : {"dir": "out", "type": "VARCHAR"}
  , ":bind2" : {"dir": "int", "type": "INTEGER", "value": 1}
  }
  */
  JDBCConn.prototype.executeUpdateWithBinds = function(sql, binds, callback) {
    var self = this;
    self._conn.prepareCall(sql, function(err, statement) {
      if (err) {
        return callback(err);
      } else {
        for (var bindName in binds) {
          if(binds.hasOwnProperty(bindName)){
            var bind = binds[bindName];
            if (bind.dir === "out") {
              if (bind.type === "VARCHAR") {
                statement.registerOutParameterSync(bindName, TYPES.VARCHAR);
              }
              else if (bind.type === "INTEGER") {
                statement.registerOutParameterSync(bindName, TYPES.INTEGER);
              }
            }
            else if (bind.dir === "in") {
              if (bind.type === "VARCHAR") {
                statement.setStringSync(bindName, bind.value);
              }
              else if (bind.type === "INTEGER") {
                statement.setIntSync(bindName, parseInt(bind.value));
              }
            }
          }
        }
        statement.executeUpdate(function(err, rowcount) {
          if (err) {
            return callback(err);
          }
          else {
            var outBinds = {};
            for (var bindName in binds) {
              if(binds.hasOwnProperty(bindName)){
                var bind = binds[bindName];
                if (bind.dir === "out") {
                  if (bind.type === "VARCHAR") {
                    outBinds[bindName] = statement.getStringSync(bindName);
                  }
                  else if (bind.type === "INTEGER") {
                    outBinds[bindName] = statement.getIntSync(bindName);
                  }
                }
              }
            }
            return callback(null, outBinds, rowcount);
          }
        });
      }
    });
  };
  
  JDBCConn.prototype.execute = function(sql, callback) {
    var self = this;
  
    self._conn.createStatement(function(err, statement) {
      if (err) {
        return callback(err);
      } else {
        statement.execute(sql, function(err, rowcount) {
          if (err) {
            return callback(err);
          } else {
            return callback(null, rowcount);
          }
        });
      }
    });
  };
  
  module.exports = JDBCConn;
  