jdbc
Version:
Node Module JDBC wrapper
277 lines (237 loc) • 7.05 kB
JavaScript
/* jshint node: true */
;
var _ = require('lodash');
var asyncjs = require('async');
var uuid = require('uuid');
var jinst = require("./jinst");
var dm = require('./drivermanager');
var Connection = require('./connection');
var winston = require('winston');
var java = jinst.getInstance();
if (!jinst.isJvmCreated()) {
jinst.addOption("-Xrs");
}
var keepalive = function(conn, query) {
var self = this;
conn.createStatement(function(err, statement) {
if (err) return winston.error(err);
statement.execute(query, function(err, result) {
if (err) return winston.error(err);
winston.silly("%s - Keep-Alive", new Date().toUTCString());
});
});
};
var addConnection = function(url, props, ka, maxIdle, callback) {
dm.getConnection(url, props, function(err, conn) {
if (err) {
return callback(err);
} else {
var connobj = {
uuid: uuid.v4(),
conn: new Connection(conn),
keepalive: ka.enabled ? setInterval(keepalive, ka.interval, conn, ka.query) : false
};
if (maxIdle) {
connobj.lastIdle = new Date().getTime();
}
return callback(null, connobj);
}
});
};
var addConnectionSync = function(url, props, ka, maxIdle) {
var conn = dm.getConnectionSync(url, props);
var connobj = {
uuid: uuid.v4(),
conn: new Connection(conn),
keepalive: ka.enabled ? setInterval(keepalive, ka.interval, conn, ka.query) : false
};
if (maxIdle) {
connobj.lastIdle = new Date().getTime();
}
return connobj;
};
function Pool(config) {
this._url = config.url;
this._props = (function (config) {
var Properties = java.import('java.util.Properties');
var properties = new Properties();
for(var name in config.properties) {
properties.putSync(name, config.properties[name]);
}
// NOTE: https://docs.oracle.com/javase/7/docs/api/java/util/Properties.html#getProperty(java.lang.String)
// if property does not exist it returns 'null' in the new java version, so we can use _.isNil to support
// older versions as well
if (config.user && _.isNil(properties.getPropertySync('user'))) {
properties.putSync('user', config.user);
}
if (config.password && _.isNil(properties.getPropertySync('password'))) {
properties.putSync('password', config.password);
}
return properties;
})(config);
this._drivername = config.drivername ? config.drivername : '';
this._minpoolsize = config.minpoolsize ? config.minpoolsize : 1;
this._maxpoolsize = config.maxpoolsize ? config.maxpoolsize : 1;
this._keepalive = config.keepalive ? config.keepalive : {
interval: 60000,
query: 'select 1',
enabled: false
};
this._maxidle = (!this._keepalive.enabled && config.maxidle) || null;
this._logging = config.logging ? config.logging : {
level: 'error'
};
this._pool = [];
this._reserved = [];
}
var connStatus = function(acc, pool) {
_.reduce(pool, function(conns, connobj) {
var conn = connobj.conn;
var closed = conn.isClosedSync();
var readonly = conn.isReadOnlySync();
var valid = conn.isValidSync(1000);
conns.push({
uuid: connobj.uuid,
closed: closed,
readonly: readonly,
valid: valid
});
return conns;
}, acc);
return acc;
};
Pool.prototype.status = function(callback) {
var self = this;
var status = {};
status.available = self._pool.length;
status.reserved = self._reserved.length;
status.pool = connStatus([], self._pool);
status.rpool = connStatus([], self._reserved);
callback(null, status);
};
Pool.prototype._addConnectionsOnInitialize = function(callback){
var self = this;
asyncjs.times(self._minpoolsize, function(n, next){
addConnection(self._url, self._props, self._keepalive, self._maxidle, function(err, conn) {
next(err, conn);
});
}, function(err, conns) {
if (err) {
return callback(err);
} else {
_.each(conns, function(conn) {
self._pool.push(conn);
});
return callback(null);
}
});
};
Pool.prototype.initialize = function(callback) {
var self = this;
winston.level = this._logging.level;
// If a drivername is supplied, initialize the via the old method,
// Class.forName()
if (this._drivername) {
java.newInstance(this._drivername, function(err, driver) {
if (err) {
return callback(err);
} else {
dm.registerDriver(driver, function(err) {
if (err) {
return callback(err);
}
self._addConnectionsOnInitialize(callback);
});
}
});
}
else {
self._addConnectionsOnInitialize(callback);
}
jinst.events.emit('initialized');
};
Pool.prototype.reserve = function(callback) {
var self = this;
var conn = null;
self._closeIdleConnections();
if (self._pool.length > 0 ) {
conn = self._pool.shift();
if (conn.lastIdle) {
conn.lastIdle = new Date().getTime();
}
self._reserved.unshift(conn);
} else if (self._reserved.length < self._maxpoolsize) {
try {
conn = addConnectionSync(self._url, self._props, self._keepalive, self._maxidle);
self._reserved.unshift(conn);
} catch (err) {
winston.error(err);
conn = null;
return callback(err);
}
}
if (conn === null) {
callback(new Error("No more pool connections available"));
} else {
callback(null, conn);
}
};
Pool.prototype._closeIdleConnections = function() {
if (! this._maxidle) {
return;
}
var self = this;
closeIdleConnectionsInArray(self._pool, this._maxidle);
closeIdleConnectionsInArray(self._reserved, this._maxidle);
};
function closeIdleConnectionsInArray(array, maxIdle) {
var time = new Date().getTime();
var maxLastIdle = time - maxIdle;
for (var i = array.length - 1; i >=0; i--) {
var conn = array[i];
if (typeof conn === 'object' && conn.conn !== null) {
if (conn.lastIdle < maxLastIdle) {
conn.conn.close(function(err) { });
array.splice(i, 1);
}
}
}
}
Pool.prototype.release = function(conn, callback) {
var self = this;
if (typeof conn === 'object') {
var uuid = conn.uuid;
self._reserved = _.reject(self._reserved, function(conn) {
return conn.uuid === uuid;
});
if (conn.lastIdle) {
conn.lastIdle = new Date().getTime();
}
self._pool.unshift(conn);
return callback(null);
} else {
return callback(new Error("INVALID CONNECTION"));
}
};
Pool.prototype.purge = function(callback) {
var self = this;
var conns = self._pool.concat(self._reserved);
asyncjs.each(conns,
function(conn, done) {
if (typeof conn === 'object' && conn.conn !== null) {
conn.conn.close(function(err) {
//we don't want to prevent other connections from being closed
done();
});
} else {
done();
}
},
function() {
self._pool = [];
self._reserved = [];
callback();
}
);
};
module.exports = Pool;