Handle<Value> OracleClient::ConnectSync(const Arguments& args) {
  HandleScope scope;
  REQ_OBJECT_ARG(0, settings);

  OracleClient* client = ObjectWrap::Unwrap<OracleClient>(args.This());
  ConnectBaton baton(client, client->m_environment, NULL);

  OBJ_GET_STRING(settings, "hostname", baton.hostname);
  OBJ_GET_STRING(settings, "user", baton.user);
  OBJ_GET_STRING(settings, "password", baton.password);
  OBJ_GET_STRING(settings, "database", baton.database);
  OBJ_GET_NUMBER(settings, "port", baton.port, 1521);

  try {
    char connectionStr[512];
    snprintf(connectionStr, sizeof(connectionStr), "//%s:%d/%s", baton.hostname.c_str(), baton.port, baton.database.c_str());
    std::string connStr = std::string(connectionStr);
    baton.connection = baton.environment->createConnection(baton.user, baton.password, connStr);
  } catch(oracle::occi::SQLException &ex) {
    return scope.Close(ThrowException(Exception::Error(String::New(ex.getMessage().c_str()))));
  } catch (const std::exception& ex) {
    return scope.Close(ThrowException(Exception::Error(String::New(ex.what()))));
  }

  Handle<Object> connection = Connection::constructorTemplate->GetFunction()->NewInstance();
      (node::ObjectWrap::Unwrap<Connection>(connection))->setConnection(baton.client->m_environment, NULL, baton.connection);

  return scope.Close(connection);

}
Handle<Value> OracleClient::Connect(const Arguments& args) {
  HandleScope scope;

  REQ_OBJECT_ARG(0, settings);
  REQ_FUN_ARG(1, callback);

  OracleClient* client = ObjectWrap::Unwrap<OracleClient>(args.This());
  connect_baton_t* baton = new connect_baton_t();
  baton->client = client;
  baton->callback = Persistent<Function>::New(callback);
  baton->environment = client->m_environment;

  OBJ_GET_STRING(settings, "connection", baton->connectionStr);
  OBJ_GET_STRING(settings, "hostname", baton->hostname);
  OBJ_GET_STRING(settings, "user", baton->user);
  OBJ_GET_STRING(settings, "password", baton->password);
  OBJ_GET_STRING(settings, "database", baton->database);
  OBJ_GET_NUMBER(settings, "port", baton->port, 1521);

  client->Ref();

  uv_work_t* req = new uv_work_t();
  req->data = baton;
  uv_queue_work(uv_default_loop(), req, EIO_Connect, (uv_after_work_cb)EIO_AfterConnect);

  return Undefined();
}
uni::CallbackType OracleClient::ConnectSync(const uni::FunctionCallbackInfo& args) {
  UNI_SCOPE(scope);
  REQ_OBJECT_ARG(0, settings);

  OracleClient* client = ObjectWrap::Unwrap<OracleClient>(args.This());
  ConnectBaton baton(client, client->m_environment, NULL);

  OBJ_GET_STRING(settings, "hostname", baton.hostname);
  OBJ_GET_STRING(settings, "user", baton.user);
  OBJ_GET_STRING(settings, "password", baton.password);
  OBJ_GET_STRING(settings, "database", baton.database);
  OBJ_GET_NUMBER(settings, "port", baton.port, 1521);

  try {
    std::ostringstream connectionStr;
    connectionStr << "//" << baton.hostname << ":" << baton.port << "/" << baton.database;
    baton.connection = baton.environment->createConnection(baton.user, baton.password, connectionStr.str());
  } catch(oracle::occi::SQLException &ex) {
    baton.error = new std::string(ex.getMessage());
    UNI_THROW(Exception::Error(NanNew<String>(baton.error->c_str())));
  } catch (const std::exception& ex) {
    UNI_THROW(Exception::Error(NanNew<String>(ex.what())));
  }

  Handle<Object> connection = uni::Deref(Connection::constructorTemplate)->GetFunction()->NewInstance();
      (node::ObjectWrap::Unwrap<Connection>(connection))->setConnection(baton.client->m_environment, baton.connection);

  UNI_RETURN(scope, args, connection);

}
Handle<Value> OracleClient::CreateConnectionPool(const Arguments& args) {
  HandleScope scope;

  REQ_OBJECT_ARG(0, settings);
  REQ_FUN_ARG(1, callback);

  OracleClient* client = ObjectWrap::Unwrap<OracleClient>(args.This());
  ConnectBaton* baton = new ConnectBaton(client, client->m_environment, &callback);

  uint32_t busyOption;

  OBJ_GET_STRING(settings, "hostname", baton->hostname);
  OBJ_GET_STRING(settings, "user", baton->user);
  OBJ_GET_STRING(settings, "password", baton->password);
  OBJ_GET_STRING(settings, "database", baton->database);
  OBJ_GET_NUMBER(settings, "port", baton->port, 1521);
  OBJ_GET_NUMBER(settings, "minConn", baton->minConn, 0);
  OBJ_GET_NUMBER(settings, "maxConn", baton->maxConn, 4);
  OBJ_GET_NUMBER(settings, "incrConn", baton->incrConn, 1);
  OBJ_GET_NUMBER(settings, "timeout", baton->timeout, 0);
  OBJ_GET_NUMBER(settings, "busyOption", busyOption, 0);
  baton->busyOption = static_cast<oracle::occi::StatelessConnectionPool::BusyOption>(busyOption);
  OBJ_GET_STRING(settings, "tns", baton->tns);


  client->Ref();

  uv_work_t* req = new uv_work_t();
  req->data = baton;
  uv_queue_work(uv_default_loop(), req, EIO_CreateConnectionPool, (uv_after_work_cb)EIO_AfterCreateConnectionPool);

  return scope.Close(Undefined());
}
Handle<Value> OracleClient::CreateConnectionPoolSync(const Arguments& args) {
  HandleScope scope;
  REQ_OBJECT_ARG(0, settings);

  OracleClient* client = ObjectWrap::Unwrap<OracleClient>(args.This());

  std::string hostname, user, password, database, tns;
  unsigned int port, minConn, maxConn, incrConn, timeout, busyOption;

  OBJ_GET_STRING(settings, "hostname", hostname);
  OBJ_GET_STRING(settings, "user", user);
  OBJ_GET_STRING(settings, "password", password);
  OBJ_GET_STRING(settings, "database", database);
  OBJ_GET_NUMBER(settings, "port", port, 1521);
  OBJ_GET_NUMBER(settings, "minConn", minConn, 0);
  OBJ_GET_NUMBER(settings, "maxConn", maxConn, 4);
  OBJ_GET_NUMBER(settings, "incrConn", incrConn, 1);
  OBJ_GET_NUMBER(settings, "timeout", timeout, 0);
  OBJ_GET_NUMBER(settings, "busyOption", busyOption, 0);
  OBJ_GET_STRING(settings, "tns", tns);

  try {
    char connectionStr[512];
    if (tns != "") {
      snprintf(connectionStr, sizeof(connectionStr), "%s", tns.c_str());
    } else {
      snprintf(connectionStr, sizeof(connectionStr), "//%s:%d/%s", hostname.c_str(), port, database.c_str());
    }
    std::string connStr = std::string(connectionStr);
    oracle::occi::StatelessConnectionPool *scp = client->m_environment->createStatelessConnectionPool(
                                    user, password, connStr, maxConn,
                                    minConn, incrConn,
                                    oracle::occi::StatelessConnectionPool::HOMOGENEOUS);
    scp->setTimeOut(timeout);
    scp->setBusyOption(static_cast<oracle::occi::StatelessConnectionPool::BusyOption>(busyOption));

    Handle<Object> connectionPool = ConnectionPool::connectionPoolConstructorTemplate->GetFunction()->NewInstance();
    (node::ObjectWrap::Unwrap<ConnectionPool>(connectionPool))->setConnectionPool(client->m_environment, scp);

    return scope.Close(connectionPool);
  } catch(oracle::occi::SQLException &ex) {
    return scope.Close(ThrowException(Exception::Error(String::New(ex.getMessage().c_str()))));
  } catch (const std::exception& ex) {
    return scope.Close(ThrowException(Exception::Error(String::New(ex.what()))));
  }
}
Handle<Value> OracleClient::Connect(const Arguments& args) {
  HandleScope scope;

  REQ_OBJECT_ARG(0, settings);
  REQ_FUN_ARG(1, callback);

  OracleClient* client = ObjectWrap::Unwrap<OracleClient>(args.This());
  ConnectBaton* baton = new ConnectBaton(client, client->m_environment, &callback);

  OBJ_GET_STRING(settings, "hostname", baton->hostname);
  OBJ_GET_STRING(settings, "user", baton->user);
  OBJ_GET_STRING(settings, "password", baton->password);
  OBJ_GET_STRING(settings, "database", baton->database);
  OBJ_GET_NUMBER(settings, "port", baton->port, 1521);
  OBJ_GET_STRING(settings, "tns", baton->tns);

  client->Ref();

  uv_work_t* req = new uv_work_t();
  req->data = baton;
  uv_queue_work(uv_default_loop(), req, EIO_Connect, (uv_after_work_cb)EIO_AfterConnect);

  return scope.Close(Undefined());
}
uni::CallbackType OracleClient::Connect(const uni::FunctionCallbackInfo& args) {
  UNI_SCOPE(scope);

  REQ_OBJECT_ARG(0, settings);
  REQ_FUN_ARG(1, callback);

  OracleClient* client = ObjectWrap::Unwrap<OracleClient>(args.This());
  ConnectBaton* baton = new ConnectBaton(client, client->m_environment, &callback);

  OBJ_GET_STRING(settings, "hostname", baton->hostname);
  OBJ_GET_STRING(settings, "user", baton->user);
  OBJ_GET_STRING(settings, "password", baton->password);
  OBJ_GET_STRING(settings, "database", baton->database);
  OBJ_GET_NUMBER(settings, "port", baton->port, 1521);
  OBJ_GET_STRING(settings, "tns", baton->tns);

  client->Ref();

  uv_work_t* req = new uv_work_t();
  req->data = baton;
  uv_queue_work(uv_default_loop(), req, EIO_Connect, (uv_after_work_cb)EIO_AfterConnect);

  UNI_RETURN(scope, args, NanUndefined());
}