Permalink
Browse files

events (but notworking)

  • Loading branch information...
1 parent 2ae4bf1 commit 403bbf962bdd80a9e69103c4247dac7a42c2aadc @apb2006 committed Jun 2, 2012
View
5 changelog.md
@@ -1,3 +1,8 @@
+## v0.5.0 - 2012-06-04
+
+- Support for BaseX events
+- Rewrite of parser
+
## v0.4.1 - 2011-11-09
- err set in callbacks
View
19 debug.js
@@ -0,0 +1,19 @@
+/* example functions for use as test callbacks
+* andy bunce 2011-2012
+*/
+function print(err, reply) {
+ if (err) {
+ console.log("Error: " + err);
+ } else {
+ console.dir(reply);
+ }
+};
+// show supplied msg then basex server response
+function printMsg(msg) {
+ return function(err, reply){
+ console.log("printMsg: ",msg);
+ if(arguments.length==2) print(err, reply)
+ }
+};
+exports.print = print;
+exports.printMsg = printMsg;
View
19 docs/commands.md
@@ -8,7 +8,7 @@ The second `reply` will hold the reply. The reply is often an object with
{ result: '1 2 3 4 5 6 7 8 9 10',
info: '\nQuery executed in 0.38 ms.\n' }
-The utility function `print` shows the syntax:
+The utility function `print` in the debug module shows the syntax:
function print(err, reply) {
if (err) {
@@ -21,16 +21,17 @@ The utility function `print` shows the syntax:
## Create a Client Session
var basex=require("basex");
var session=basex.Session(host, port, username, password)
-hostname (default="localhost")
-port (default=1984)
-username (default="admin")
-password (default= "admin")
+The default values are:
+ hostname (default="localhost")
+ port (default=1984)
+ username (default="admin")
+ password (default= "admin")
## Session commands
//Executes a command and returns the result:
session.execute(command,callback)
- //Returns a query object for the specified query:
+ //Returns a query object for the specified query, see below for more detail:
var query=session.query(query)
//Creates a database from an input stream:
@@ -46,7 +47,7 @@ password (default= "admin")
session.store( path, in,callback)
//Watches the specified event:
- session.watch( name, callback)
+ session.watch( name, notification,callback)
//Unwatches the specified event:
session.unwatch( name,callback)
@@ -55,7 +56,7 @@ password (default= "admin")
session.info(callback)
//Closes the session:
- session.close()
+ session.close(callback)
## The query object
@@ -64,6 +65,8 @@ password (default= "admin")
query.close();
+ query.results(callback);
+
query.execute(callback);
query.info(callback);
View
13 examples/AddExample.js
@@ -3,23 +3,24 @@
*
*/
var basex = require("../index");
+var log = require("../debug");
// create session
var client = new basex.Session("localhost", 1984, "admin", "admin");
-basex.debug_mode = true;
+basex.debug_mode = false;
// create new database
-client.execute("create db database", basex.print);
+client.execute("create db database", log.print);
// add document
-client.add("/world/World.xml", "<x>Hello World!</x>", basex.print);
+client.add("/world/World.xml", "<x>Hello World!</x>", log.print);
// add document
-client.add("Universe.xml", "<x>Hello Universe!</x>", basex.print);
+client.add("Universe.xml", "<x>Hello Universe!</x>", log.print);
// run query on database
-client.execute("xquery /", basex.print);
+client.execute("xquery /", log.print);
// drop database
-client.execute("drop db database", basex.print);
+client.execute("drop db database", log.print);
// close session
client.close();
View
9 examples/BadExample.js
@@ -3,19 +3,20 @@
*
*/
var basex = require("../index");
+var log = require("../debug");
// create session
var client = new basex.Session("localhost", 1984, "admin", "admin");
basex.debug_mode = true;
// create new database
-client.execute("create db database", basex.print);
+client.execute("create db database", log.print);
//run query on database
-client.execute("xquery 2+2", basex.print);
+client.execute("xquery 2+2", log.print);
// run query on database
-client.execute("xquery 2+", basex.print);
+client.execute("xquery 2+", log.print);
// drop database
-client.execute("drop db database", basex.print);
+client.execute("drop db database", log.print);
// close session
client.close();
View
7 examples/CreateExample.js
@@ -3,18 +3,19 @@
*
*/
var basex = require("../index");
+var log = require("../debug");
//basex.debug_mode = true;
// create session
var client = new basex.Session("localhost", 1984, "admin", "admin");
// create new database
-client.create("database", "<x>Hello World!</x>", basex.print);
+client.create("database", "<x>Hello World!</x>", log.print);
// run query on database
-client.execute("xquery /", basex.print);
+client.execute("xquery /", log.print);
// drop database
-client.execute("drop db database", basex.print);
+client.execute("drop db database", log.print);
// close session
client.close();
View
38 examples/EventExample.js
@@ -0,0 +1,38 @@
+/*
+ * This example shows how to use the event feature.
+ *
+ */
+var basex = require("../index");
+var d = require("../debug");
+
+// create sessions
+var session1 = new basex.Session("localhost", 1984, "admin", "admin");
+var session2 = new basex.Session("localhost", 1984, "admin", "admin");
+basex.debug_mode = false;
+
+function watchCallback(name,msg){
+ console.log("watch update-----> ",msg)
+ session2.unwatch("messenger",function(){
+ session1.execute("drop event messenger",d.printMsg("S1:drop event"));
+ // close session
+ session1.close(d.printMsg("S1:close"));
+ session2.close(d.printMsg("S2:close"));
+ });
+
+};
+
+function afterEvent(err, reply){
+ if (err) {
+ console.log("Error: " + err);
+ //return;
+ }
+ console.dir(reply);
+ session2.watch("messenger",watchCallback,d.printMsg("S2:watching messenger"));
+ var xq="for $i in 1 to 1000000 where $i=3 return $i"
+ session2.query(xq).execute(d.printMsg("S2:execute"));
+ session1.query("db:event('messenger', 'Hello World!')").execute(d.printMsg("S1:event"));
+
+};
+
+session1.execute("create event messenger",afterEvent);
+
View
9 examples/Example.js
@@ -10,12 +10,15 @@ function print(err, reply) {
console.log("Error: " + err);
} else {
var t2=new Date();
- console.log("Commands completed in ",t2-t0," milliseconds.");
+ console.log("Execution completed in ",t2-t0," milliseconds.");
}
};
var t0=new Date();
-client.execute("xquery 1 to 10",basex.print);
-client.close(print);
+client.execute("xquery 1 to 10",print);
+client.close(function(){
+ var t2=new Date();
+ console.log("Closed in ",t2-t0," milliseconds.");
+});
var t1=new Date();
// not a true time because basex commands not yet done.
console.log("Commands send in ",t1-t0," milliseconds.");
View
11 examples/QueryBindExample.js
@@ -4,6 +4,7 @@
*/
var basex = require("../index");
+var log = require("../debug");
//basex.debug_mode = true;
var session = new basex.Session("localhost", 1984, "admin", "admin");
@@ -12,14 +13,14 @@ var input = "declare variable $name external; for $i in 1 to 10 return element {
var query = session.query(input);
// bind variable
-query.bind("name", "nodex","",basex.print);
+query.bind("name", "nodex","",log.print);
// print results
-query.execute(basex.print);
+query.execute(log.print);
// do it again
-query.bind("name", "again","",basex.print);
-query.execute(basex.print);
-query.info(basex.print);
+query.bind("name", "again","",log.print);
+query.execute(log.print);
+query.info(log.print);
// close query instance
query.close();
View
5 examples/QueryExample.js
@@ -5,15 +5,16 @@
*
*/
var basex = require("../index");
-basex.debug_mode = true;
+var log = require("../debug");
+basex.debug_mode = false;
// create session
var session = new basex.Session("localhost", 1984, "admin", "admin");
// create query instance
var input = 'for $i in 1 to 10 return <xml>Text { $i }</xml>';
var query = session.query(input);
-query.iter(basex.print);
+query.results(log.print);
//query.info(basex.print);
//query.options(basex.print);
// loop through all results
View
58 examples/buffer_bench.js
@@ -1,58 +0,0 @@
-var source = new Buffer(100),
- dest = new Buffer(100), i, j, k, tmp, count = 1000000, bytes = 100;
-
-for (i = 99 ; i >= 0 ; i--) {
- source[i] = 120;
-}
-
-for (i = bytes ; i > 0 ; i --) {
- var start = new Date();
- for (j = count ; j > 0; j--) {
- tmp = source.toString("ascii", 0, bytes);
- }
- var end = new Date();
- console.log("toString() " + i + " bytes " + (end - start) + " ms");
-}
-
-for (i = bytes ; i > 0 ; i --) {
- var start = new Date();
- for (j = count ; j > 0; j--) {
- tmp = "";
- for (k = 0; k <= i ; k++) {
- tmp += String.fromCharCode(source[k]);
- }
- }
- var end = new Date();
- console.log("manual string " + i + " bytes " + (end - start) + " ms");
-}
-
-for (i = bytes ; i > 0 ; i--) {
- var start = new Date();
- for (j = count ; j > 0 ; j--) {
- for (k = i ; k > 0 ; k--) {
- dest[k] = source[k];
- }
- }
- var end = new Date();
- console.log("Manual copy " + i + " bytes " + (end - start) + " ms");
-}
-
-for (i = bytes ; i > 0 ; i--) {
- var start = new Date();
- for (j = count ; j > 0 ; j--) {
- for (k = i ; k > 0 ; k--) {
- dest[k] = 120;
- }
- }
- var end = new Date();
- console.log("Direct assignment " + i + " bytes " + (end - start) + " ms");
-}
-
-for (i = bytes ; i > 0 ; i--) {
- var start = new Date();
- for (j = count ; j > 0 ; j--) {
- source.copy(dest, 0, 0, i);
- }
- var end = new Date();
- console.log("Buffer.copy() " + i + " bytes " + (end - start) + " ms");
-}
View
22 examples/npm-debug.log
@@ -0,0 +1,22 @@
+info it worked if it ends with ok
+verbose cli [ 'node', '/usr/bin/npm', 'test' ]
+info using npm@1.0.105
+info using node@v0.6.10
+verbose /usr/bin/node node symlink
+verbose config file /home/andy/.npmrc
+verbose config file /usr/etc/npmrc
+verbose config file /usr/lib/node_modules/npm/npmrc
+ERR! Error: ENOENT, no such file or directory '/home/andy/workspace/basex-node/examples/package.json'
+ERR! Report this *entire* log at:
+ERR! <http://github.com/isaacs/npm/issues>
+ERR! or email it to:
+ERR! <npm-@googlegroups.com>
+ERR!
+ERR! System Linux 3.0.0-19-generic
+ERR! command "node" "/usr/bin/npm" "test"
+ERR! cwd /home/andy/workspace/basex-node/examples
+ERR! node -v v0.6.10
+ERR! npm -v 1.0.105
+ERR! path /home/andy/workspace/basex-node/examples/package.json
+ERR! code ENOENT
+verbose exit [ 1, true ]
View
11 examples/simple.js
@@ -1,13 +1,14 @@
// standalone basex test
var basex =require("../index");
+var log = require("../debug");
basex.debug_mode = true;
var s=new basex.Session();
-s.execute("info",basex.print);
-s.execute("list",basex.print);
-s.execute("OPEN factbook",basex.print);
-s.execute("XQUERY 1 to 5",basex.print);
-s.execute("XQUERY count(//*)",basex.print);
+s.execute("info",log.print);
+s.execute("list",log.print);
+s.execute("OPEN factbook",log.print);
+s.execute("XQUERY 1 to 5",log.print);
+s.execute("XQUERY count(//*)",log.print);
s.close();
View
225 index.js
@@ -1,15 +1,18 @@
-/* baseX client
- * andy bunce 2011
- */
+/* BaseX Node.js client
+ * http://docs.basex.org/wiki/Server_Protocol
+* andy bunce 2011-2012
+*/
-// can set this to true to enable for all connections
+// set this to true to enable console.log msgs for all connections
exports.debug_mode = false;
var net = require("net")
, util = require("util")
, events = require("events")
, crypto = require("crypto")
, assert = require('assert')
+ , Query = require("./lib/query").Query
+ , parser = require("./lib/parser")
, Queue = require("./lib/queue").Queue;
var states = {
@@ -20,30 +23,37 @@ var states = {
CLOSING : 4
};
-var CHR0 = "\x00";
+var CHR0 = "\0";
+var tagid=0; // used to give each BaseXStream a unique .tag property
var BaseXStream = function(host, port, username, password) {
var self = this;
this.port = port || 1984;
this.host = host || "127.0.0.1";
this.username = username || "admin";
this.password = password || "admin";
- this.connections = 0;
- this.attempts = 1;
-
+ this.tag ="S"+(++tagid);
this.commands_sent = 0;
- this.retry_delay = 250;
- this.retry_backoff = 1.7;
- this.subscriptions = false;
+ // reset
this.reset = function() {
this.state = states.DISCONNECTED;
- this.current_cmd = null;
+ this.current_command = null; //waiting for response to this
this.closefn = null;
this.blocked=false; //can't send until reply
this.buffer = "";
this.q_pending = new Queue(); // holds commands to send
this.q_sent = new Queue(); // holds commands sent
-
+ // event stuff
+ this.event={stream:null,
+ en:{}, //names to notifiers
+ buffer:"",
+ parse:function(){ // intial \0 from event
+ var r=parser.popmsg(self.event,[])
+ if(!r.ok)throw "Bad event protocol";
+ self.event.parse=self.parseEvent;
+ }
+ };
+ // initial parser for auth
this.parser = function() {
var timestamp = self.readline();
self.send(self.username);
@@ -60,15 +70,15 @@ var BaseXStream = function(host, port, username, password) {
stream.on("connect", function() {
self.state = states.CONNECTING;
if (exports.debug_mode) {
- console.log("stream connected");
+ console.log(self.tag+": stream connected");
}
;
});
stream.on("data", function(reply) {
self.buffer += reply;
if (exports.debug_mode) {
- console.log("<<");
+ console.log(self.tag+"<<");
console.dir(self.buffer);
}
;
@@ -81,7 +91,7 @@ var BaseXStream = function(host, port, username, password) {
throw "Access denied.";
self.state = states.CONNECTED;
if (exports.debug_mode) {
- console.log("authorized");
+ console.log(self.tag+": authorized");
}
;
self.emit("connected", 1);
@@ -115,9 +125,13 @@ var BaseXStream = function(host, port, username, password) {
stream.on("close", function() {
if (exports.debug_mode) {
- console.log("stream closed");
+ console.log(self.tag+": stream closed");
+ }
+ if(self.event.stream){
+ //console.log(self.tag+": event stream closing");
+ self.event.stream.destroySoon();
+ self.event.stream=null;
}
- ;
if (self.closefn) {
self.closefn();
self.closefn = null;
@@ -126,7 +140,7 @@ var BaseXStream = function(host, port, username, password) {
});
stream.on("end", function() {
- console.log("stream end");
+ //console.log(self.tag+": stream end");
});
stream.on("drain", function() {
@@ -137,7 +151,7 @@ var BaseXStream = function(host, port, username, password) {
if (typeof s === "function") s = s();
if (exports.debug_mode) {
- console.log(">>");
+ console.log(self.tag+">>");
console.dir(s + CHR0);
}
;
@@ -157,29 +171,38 @@ var BaseXStream = function(host, port, username, password) {
// read upto null
this.readline = function() {
var p = self.buffer.indexOf(CHR0);
- assert.notEqual(p, -1, "no null");
+ if(p==-1){
+ console.dir(self.current_command);
+ console.dir(self.buffer);
+ assert.notEqual(p, -1, "no null");
+ }
+
// console.log("data", l, p, buffer + ":");
var ip = self.buffer.substring(0, p);
self.buffer = self.buffer.substring(p + 1);
return ip;
};
// standard parser read 2 lines and byte
this.parser1= function() {
- if (-1 != self.buffer.indexOf(CHR0)) {
- var result = self.readline();
- var info = self.readline();
- var ok = self.ok();
- return {
- result : result,
- info : info,
- ok : ok
- };
- }
-
+ return parser.popmsg(self,["result","info"])
+ };
+ // read status byte
+ this.parserOk= function() {
+ return parser.popmsg(self,[])
};
+ // watch response
+ this.parseEvent=function(){
+ var r=parser.popmsg(self.event,["name","msg"],false)
+ if(r){
+ var f=self.event.en[r.name];
+ f(r.name,r.msg)
+ }
+ };
// read line and byte
this.parser2 = function() {
if (-1 != self.buffer.indexOf(CHR0)) {
+ //console.log(self.tag," parser2",self.current_command.send)
+ //console.dir(self.buffer)
var reply = self.readline();
var ok = self.ok();
var r={ok:ok};
@@ -209,6 +232,30 @@ var BaseXStream = function(host, port, username, password) {
return r
}
};
+ // parse watch response
+ this.parsewatch=function(){
+ // expect port,id,info,ok or info,ok
+ var flds=parser.popmsg(self,["eport","id","info"])
+ if (flds) {
+ //console.log(flds)
+ //console.dir(parts)
+ if(self.event.stream == null){
+ var stream = net.createConnection(flds.eport, self.host);
+ stream.setEncoding('utf-8');
+ stream.on("data", function(reply) {
+ if (exports.debug_mode) {
+ console.log("event<<");
+ console.dir(reply);
+ };
+ self.event.buffer+=reply;
+ self.event.parse()
+ });
+ self.event.stream = stream;
+ stream.write(flds.id + CHR0);
+ }
+ return flds;
+ }
+ };
// add command and returns the result:
this.execute = function(cmd, callback) {
@@ -255,29 +302,28 @@ var BaseXStream = function(host, port, username, password) {
});
};
// watch
- this.watch = function(name, callback) {
- throw "@TODO watch";
+ this.watch = function(name,notification,callback) {
+ self.event.en[name]=notification;
self.send_command({
send : "\x0A" + name,
- parser : self.parser1,
- callback : callback
+ parser : self.parsewatch,
+ callback :callback
});
};
// unwatch
this.unwatch = function(name, callback) {
- throw "@TODO unwatch";
self.send_command({
send : "\x0B" + name,
- parser : self.parser1,
+ parser : self.parser2,
callback : callback
});
};
// end the session, sash callback, for stream end
this.close = function(callback) {
self.closefn = callback;
self.send_command({
- send : "exit" + CHR0,
- parser : self.parser1,
+ send : "exit",
+ parser : self.parserOk //sometime get this, timing
});
};
// -------end of commands ---------
@@ -287,7 +333,7 @@ var BaseXStream = function(host, port, username, password) {
self.q_pending.push(cmd);
self.sendQueueItem();
};
- // do the next queued command, if any, true true if sent
+ // do the next queued command, if any, return true if sent
this.sendQueueItem = function() {
//console.log("queues waiting send: ",self.q_pending.length,", waiting reply:",self.q_sent.length)
@@ -299,6 +345,8 @@ var BaseXStream = function(host, port, username, password) {
var cmd = self.q_pending.shift();
if(!self.current_command){
self.current_command=cmd;
+ //console.log("current command: ",cmd.send)
+
}else{
self.q_sent.push(cmd);
};
@@ -317,105 +365,10 @@ var BaseXStream = function(host, port, username, password) {
events.EventEmitter.call(this);
};
-// query
-// send is function as needs id
-var Query = function(session, query) {
- this.session = session;
- this.query = query;
- this.id = null;
- var self = this;
-
- this.close = function(callback) {
- self.session.send_command({
- send : function() {
- return "\x02" + self.id
- },
- parser : self.session.parser2,
- callback : callback
- });
- };
-
- this.bind = function(name, value, type, callback) {
- var type = "";
- self.session.send_command({
- send : function() {
- return "\x03" + self.id + "\x00" + name + "\x00" + value
- + "\x00" + type
- },
- parser : self.session.parser2,
- callback : callback
- });
-
- };
-
- this.iter = function(callback) {
- self.session.send_command({
- send : function() {
- return "\x04" + self.id
- },
- parser : self.session.readiter,
- callback : callback
- });
- };
-
- this.execute = function(callback) {
- self.session.send_command({
- send : function() {
- return "\x05" + self.id
- },
- parser : self.session.parser2,
- callback : callback
- });
- };
-
- this.info = function(callback) {
- self.session.send_command({
- send : function() {
- return "\x06" + self.id
- },
- parser : self.session.parser2,
- callback : callback
- });
- };
-
- this.options = function(callback) {
- self.session.send_command({
- send : function() {
- return "\x07" + self.id
- },
- parser : self.session.parser2,
- callback : callback
- });
- };
-
- // request id
- session.send_command({
- blocking : true,
- send : CHR0 + query,
- parser : self.session.parser2,
- callback : function(err, reply) {
- self.id = reply.result;
- if (exports.debug_mode) {
- console.log("Query id: ", self.id, ", query: ", query);
- }
- ;
- self.session.setBlock(false);
- }
- });
-};
function md5(str) {
return crypto.createHash('md5').update(str).digest("hex");
};
util.inherits(BaseXStream, events.EventEmitter);
exports.Session = BaseXStream;
-// debug util
-function print(err, reply) {
- if (err) {
- console.log("Error: " + err);
- } else {
- console.dir(reply);
- }
-};
-exports.print = print;
View
485 index.old.js
@@ -1,485 +0,0 @@
-/*global Buffer require exports console setTimeout */
-
-var net = require("net")
-, util = require("util")
-, Queue = require("./lib/queue").Queue
-, events = require("events")
-, crypto = require("crypto")
-, parsers = []
-, default_port = 1984
-, default_host = "127.0.0.1"
-, default_options = {
- "username" : "admin",
- "password" : "admin"
-};
-var CHR0 = "\x00";
-// can set this to true to enable for all connections
-exports.debug_mode = false;
-
-parsers.push(require("./lib/parser/javascript"));
-
-function to_array(args) {
- var len = args.length, arr = new Array(len), i;
-
- for (i = 0; i < len; i += 1) {
- arr[i] = args[i];
- }
-
- return arr;
-}
-
-function BasexClient(stream, options) {
- this.stream = stream;
- this.options = options || {};
-
- this.ready = false;
- this.connections = 0;
- this.attempts = 1;
- this.command_queue = new Queue(); // holds sent commands to de-pipeline
- // them
- this.offline_queue = new Queue(); // holds commands issued but not able to
- // be sent
- this.commands_sent = 0;
- this.retry_delay = 250;
- this.retry_backoff = 1.7;
- this.subscriptions = false;
- this.monitoring = false;
- this.closing = false;
- this.server_info = {};
- this.states = {
- DISCONNECTED: 0
- ,CONNECTING: 1
- ,AUTHORIZE:2
- ,CONNECTED:3
- };
-
- this.state = this.states.DISCONNECTED;
-
- var parser_module, self = this;
-
- if (exports.debug_mode) {
- console.log("Using default parser module: " + parsers[0].name);
- }
- parser_module = parsers[0];
-
- parser_module.debug_mode = exports.debug_mode;
- this.reply_parser = new parser_module.Parser({
- return_buffers : self.options.return_buffers || false
- });
-
- // "reply error" is an error sent back by basex
- this.reply_parser.on("reply error", function(reply) {
- self.return_error(new Error(reply));
- });
- this.reply_parser.on("reply", function(reply) {
- self.return_reply(reply);
- });
- // "error" is bad. Somehow the parser got confused. It'll try to reset and
- // continue.
- this.reply_parser.on("error",
- function(err) {
- self.emit("error", new Error("basex reply parser error: "
- + err.stack));
- });
-
- this.stream.on("connect", function() {
- self.on_connect();
- });
-
- this.stream.on("data", function(buffer_from_socket) {
- self.on_data(buffer_from_socket);
- });
-
- this.stream.on("error", function(msg) {
- if (this.closing) {
- return;
- }
-
- var message = "basex connection to " + self.host + ":" + self.port
- + " failed - " + msg.message;
-
- if (exports.debug_mode) {
- console.warn(message);
- }
- self.offline_queue.forEach(function(args) {
- if (typeof args[2] === "function") {
- args[2](message);
- }
- });
- self.offline_queue = new Queue();
-
- self.command_queue.forEach(function(args) {
- if (typeof args[2] === "function") {
- args[2](message);
- }
- });
- self.command_queue = new Queue();
-
- self.state = false;
- self.ready = false;
- self.emit("error", new Error(message));
- });
-
- this.stream.on("close", function() {
- self.connection_gone("close");
- });
-
- this.stream.on("end", function() {
- self.connection_gone("end");
- });
-
- this.stream.on("drain", function() {
- self.emit("drain");
- });
- //Official source is: http://docs.basex.org/wiki/Server_Protocol
- //This list needs to be updated, and perhaps auto-updated somehow.
- [
- //Creates and returns session with host, port, user name and password:
- "Session" // (String host, int port, String name, String password)
-
- //Executes a command and returns the result:
- , "execute" // (String command)
-
- //Returns a query object for the specified query:
- , "query" // (String query)
-
- //Creates a database from an input stream:
- , "create" // (String name, InputStream in)
-
- //Adds a document to the current database from an input stream:
- , "add" // (String name, String target, InputStream in)
-
- //Replaces a document with the specified input stream:
- , "replace" // (String path, InputStream in)
-
- //Stores raw data at the specified path:
- , "store" // (String path, InputStream in)
-
- //Watches the specified event:
- , "watch" // (String name, Event notifier)
-
- //Unwatches the specified event:
- , "unwatch" // (String name)
-
- //Returns process information:
- , "info"
-
- //Closes the session:
- , "close"
-
- ]
- .forEach(function(command) {
- BasexClient.prototype[command] = function() {
- var args = to_array(arguments);
- args.unshift(command); // put command at the beginning
- this.send_command.apply(this, args);
- };
- BasexClient.prototype[command.toUpperCase()] = BasexClient.prototype[command];
-
- });
- events.EventEmitter.call(this);
-}
-util.inherits(BasexClient, events.EventEmitter);
-exports.BasexClient = BasexClient;
-
-BasexClient.prototype.on_connect = function() {
- console.log("onconnect -apb");
- if (exports.debug_mode) {
- console.log("Stream connected " + this.host + ":" + this.port + " fd "
- + this.stream.fd);
- }
- var self = this;
- this.state=this.states.CONNECTING;
-
- this.ready = false;
- this.connections += 1;
- this.command_queue = new Queue();
- this.emitted_end = false;
- this.retry_timer = null;
- this.retry_delay = 250;
- this.stream.setNoDelay();
- this.stream.setTimeout(0);
-};
-
-
-BasexClient.prototype.send_offline_queue = function() {
- var command_obj;
- while (this.offline_queue.length > 0) {
- command_obj = this.offline_queue.shift();
- if (exports.debug_mode) {
- console.log("Sending offline command: " + command_obj.command);
- }
- this.send_command(command_obj.command, command_obj.args,
- command_obj.callback);
- }
- this.offline_queue = new Queue();
- // Even though items were shifted off, Queue backing store still uses memory
- // until next add
-};
-
-BasexClient.prototype.connection_gone = function(why) {
- var self = this;
-
- // If a retry is already in progress, just let that happen
- if (self.retry_timer) {
- return;
- }
-
- // Note that this may trigger another "close" or "end" event
- self.stream.destroy();
-
- if (exports.debug_mode) {
- console.warn("basex connection is gone from " + why + " event.");
- }
- self.state=self.states.DISCONNECTED;
- self.ready = false;
- self.subscriptions = false;
- self.monitoring = false;
-
- // since we are collapsing end and close, users don't expect to be called
- // twice
- if (!self.emitted_end) {
- self.emit("end");
- self.emitted_end = true;
- }
-
- self.command_queue.forEach(function(args) {
- if (typeof args[2] === "function") {
- args[2]("Server connection closed");
- }
- });
- self.command_queue = new Queue();
-
- // If this is a requested shutdown, then don't retry
- if (self.closing) {
- self.retry_timer = null;
- return;
- }
-
- if (exports.debug_mode) {
- console.log("Retry connection in " + self.retry_delay + " ms");
- }
- self.attempts += 1;
- self.emit("reconnecting", {
- delay : self.retry_delay,
- attempt : self.attempts
- });
- self.retry_timer = setTimeout(function() {
- if (exports.debug_mode) {
- console.log("Retrying connection...");
- }
- self.retry_delay = self.retry_delay * self.retry_backoff;
- self.stream.connect(self.port, self.host);
- self.retry_timer = null;
- }, self.retry_delay);
-};
-
-BasexClient.prototype.on_data = function(data) {
- if (exports.debug_mode) {
- console.log("net read " + this.host + ":" + this.port + " fd "
- + this.stream.fd + ": " + data.toString());
- }
-
- try {
- this.reply_parser.execute(data);
- } catch (err) {
- // This is an unexpected parser problem, an exception that came from the
- // parser code itself.
- // Parser should emit "error" events if it notices things are out of
- // whack.
- // Callbacks that throw exceptions will land in return_reply(), below.
- // TODO - it might be nice to have a different "error" event for
- // different types of errors
- this.emit("error", err);
- }
-};
-BasexClient.prototype.return_reply=function(reply){
- if(this.state==this.states.CONNECTING){
- this.send(options.username);
- var s=loginresponse(reply , this.options.password);
- this.state = this.states.AUTHORIZE;
- this.send(s);
- console.log("BasexClient.prototype.return_reply",s);
- }else if (this.state==this.states.AUTHORIZE){
- console.log("auth:",reply,"::");
- }
-};
-BasexClient.prototype.send=function(str){
- console.log(">>",str);
- stream.write(str+CHR0);
-};
-
-BasexClient.prototype.end = function() {
- this.stream._events = {};
- this.state=this.states.DISCONNECTED;
- this.ready = false;
- return this.stream.end();
-};
-
-BasexClient.prototype.send_command = function() {
- var command, callback, arg, args, this_args, command_obj, i, il, elem_count, stream = this.stream, buffer_args, command_str = "";
-
- this_args = to_array(arguments);
-
- if (this_args.length === 0) {
- throw new Error("send_command: not enough arguments");
- }
-
- if (typeof this_args[0] !== "string") {
- throw new Error(
- "First argument of send_command must be the command name");
- }
- command = this_args[0].toLowerCase();
-
- if (this_args[1] && Array.isArray(this_args[1])) {
- args = this_args[1];
- if (typeof this_args[2] === "function") {
- callback = this_args[2];
- }
- } else {
- if (typeof this_args[this_args.length - 1] === "function") {
- callback = this_args[this_args.length - 1];
- args = this_args.slice(1, this_args.length - 1);
- } else {
- args = this_args.slice(1, this_args.length);
- }
- }
-
- command_obj = {
- command : command,
- args : args,
- callback : callback,
- sub_command : false
- };
-
- if (!this.ready && !this.send_anyway) {
- if (exports.debug_mode) {
- console.log("Queueing " + command + " for next server connection.");
- }
- this.offline_queue.push(command_obj);
- return;
- }
-
- if (command === "subscribe" || command === "psubscribe"
- || command === "unsubscribe" || command === "punsubscribe") {
- if (this.subscriptions === false && exports.debug_mode) {
- console.log("Entering pub/sub mode from " + command);
- }
- command_obj.sub_command = true;
- this.subscriptions = true;
- } else if (command === "monitor") {
- this.monitoring = true;
- } else if (command === "quit") {
- this.closing = true;
- } else if (this.subscriptions === true) {
- throw new Error(
- "Connection in pub/sub mode, only pub/sub commands may be used");
- }
- this.command_queue.push(command_obj);
- this.commands_sent += 1;
-
- elem_count = 1;
- buffer_args = false;
-
- elem_count += args.length;
- // Probably should just scan this like a normal person. This is clever, but
- // might be slow.
- buffer_args = args.some(function(arg) {
- return arg instanceof Buffer;
- });
-
- // Always use "Multi bulk commands", but if passed any Buffer args, then do
- // multiple writes, one for each arg
- // This means that using Buffers in commands is going to be slower, so use
- // Strings if you don't already have a Buffer.
- // Also, why am I putting user documentation in the library source code?
-
- command_str = "*" + elem_count + "\r\n$" + command.length + "\r\n"
- + command + "\r\n";
-
- if (!stream.writable && exports.debug_mode) {
- console
- .log("send command: stream is not writeable, should get a close event next tick.");
- return;
- }
-
- if (!buffer_args) { // Build up a string and send entire command in one
- // write
- for (i = 0, il = args.length, arg; i < il; i += 1) {
- arg = args[i];
- if (typeof arg !== "string") {
- arg = String(arg);
- }
- command_str += "$" + Buffer.byteLength(arg) + "\r\n" + arg + "\r\n";
- }
- if (exports.debug_mode) {
- console.log("send " + this.host + ":" + this.port + " fd "
- + this.stream.fd + ": " + command_str);
- }
- stream.write(command_str);
- } else {
- if (exports.debug_mode) {
- console.log("send command: " + command_str);
- console.log("send command has Buffer arguments");
- }
- stream.write(command_str);
-
- for (i = 0, il = args.length, arg; i < il; i += 1) {
- arg = args[i];
- if (!(arg instanceof Buffer || arg instanceof String)) {
- arg = String(arg);
- }
-
- if (arg instanceof Buffer) {
- if (arg.length === 0) {
- if (exports.debug_mode) {
- console.log("Using empty string for 0 length buffer");
- }
- stream.write("$0\r\n\r\n");
- } else {
- stream.write("$" + arg.length + "\r\n");
- stream.write(arg);
- stream.write("\r\n");
- }
- } else {
- stream.write("$" + Buffer.byteLength(arg) + "\r\n" + arg
- + "\r\n");
- }
- }
- }
-};
-
-
-exports.createClient = function(port_arg, host_arg, options_arg) {
- var port = port_arg || default_port, host = host_arg || default_host, options = options_arg
- || default_options, basex_client, net_client;
-
- net_client = net.createConnection(port, host);
- net_client.setEncoding('utf-8')
- basex_client = new BasexClient(net_client, options);
-
- basex_client.port = port;
- basex_client.host = host;
-
- return basex_client;
-};
-
-// debug util
-function print(err, reply) {
- if (err) {
- console.log("Error: " + err);
- } else {
- console.log("Reply: " + reply);
- }
-};
-exports.print = print;
-
-//basex login response
-function loginresponse(timestamp, password) {
- // {username} {md5(md5(password) + timestamp)}
- var p1 = crypto.createHash('md5').update(password).digest("hex");
- var p2 = crypto.createHash('md5').update(p1 + timestamp).digest("hex");
- return p2 ;
-};
-
-exports.loginresponse = loginresponse;
View
42 lib/parser.js
@@ -0,0 +1,42 @@
+/* BaseX Node.js client parser
+ * http://docs.basex.org/wiki/Server_Protocol
+ * andy bunce 2011-2012
+ *
+ * parse incoming messages from basex server
+ * functions expect bufferobj to have a buffer property
+ * that is yet unprocessed msg stream as string
+*/
+
+// take fields named in array flds delimited by \0 from buffer
+// @param popStatus boolean if true read 1byte status 0/1 into ok:
+// @return object or empty
+function popmsg(bufferObj,flds,popStatus){
+ var i=0,last=0, buf=bufferObj.buffer,res={};
+ popStatus = (typeof popStatus == 'undefined')?true:popStatus;
+
+ while(i<buf.length && flds.length){
+ if(buf[i]=="\0"){
+ res[flds.shift()]=buf.substring(i,last)
+ last=i+1;
+ }
+ i++;
+ }
+ if(flds.length>0 ||(popStatus && i==buf.length)){
+ return
+ }
+ if(popStatus){
+ if("\0"==buf[i]){
+ res["ok"]=true;
+ }else if("\1"==buf[i]){
+ res["ok"]=false;
+ }else{
+ throw "expected status marker at"+i+"="+buf.charCodeAt(i)+ ":"+buf;
+ }
+ i++;
+ }
+ bufferObj.buffer=buf.substring(i)
+// console.log("msg extracted, remaining: ",bufferObj.buffer.length,bufferObj.buffer)
+ return res
+};
+
+exports.popmsg = popmsg;
View
89 lib/query.js
@@ -0,0 +1,89 @@
+/* BaseX Node.js client query handler
+ * http://docs.basex.org/wiki/Server_Protocol
+* andy bunce 2011-2012
+*/
+function Query(session, query) {
+ this.session = session;
+ this.query = query;
+ this.id = null;
+ var self = this;
+
+ this.close = function(callback) {
+ self.session.send_command({
+ send : function() { // send is function as needs id
+ return "\x02" + self.id
+ },
+ parser : self.session.parser2,
+ callback : callback
+ });
+ };
+
+ this.bind = function(name, value, type, callback) {
+ var type = "";
+ self.session.send_command({
+ send : function() {
+ return "\x03" + self.id + "\x00" + name + "\x00" + value
+ + "\x00" + type
+ },
+ parser : self.session.parser2,
+ callback : callback
+ });
+
+ };
+
+ this.results = function(callback) {
+ self.session.send_command({
+ send : function() {
+ return "\x04" + self.id
+ },
+ parser : self.session.readiter,
+ callback : callback
+ });
+ };
+
+ this.execute = function(callback) {
+ self.session.send_command({
+ send : function() {
+ return "\x05" + self.id
+ },
+ parser : self.session.parser2,
+ callback : callback
+ });
+ };
+
+ this.info = function(callback) {
+ self.session.send_command({
+ send : function() {
+ return "\x06" + self.id
+ },
+ parser : self.session.parser2,
+ callback : callback
+ });
+ };
+
+ this.options = function(callback) {
+ self.session.send_command({
+ send : function() {
+ return "\x07" + self.id
+ },
+ parser : self.session.parser2,
+ callback : callback
+ });
+ };
+
+ // request id
+ session.send_command({
+ blocking : true,
+ send : "\x00" + query,
+ parser : self.session.parser2,
+ callback : function(err, reply) {
+ self.id = reply.result;
+ // if (exports.debug_mode) {
+ // console.log("Query id: ", self.id, ", query: ", query);
+ // }
+ ;
+ self.session.setBlock(false);
+ }
+ });
+};
+exports.Query = Query;
View
8 lib/queue.js
@@ -1,3 +1,5 @@
+// Queue class adapted from Tim Caswell's pattern library
+// http://github.com/creationix/pattern/blob/master/lib/pattern/queue.js
function to_array(args) {
var len = args.length,
arr = new Array(len), i;
@@ -7,11 +9,7 @@ function to_array(args) {
}
return arr;
-}
-
-// Queue class adapted from Tim Caswell's pattern library
-// http://github.com/creationix/pattern/blob/master/lib/pattern/queue.js
-
+};
var Queue = function () {
this.tail = [];
this.head = to_array(arguments);
View
48 npm-debug.log
@@ -1,57 +1,57 @@
info it worked if it ends with ok
verbose cli [ 'node', '/usr/bin/npm', 'test' ]
info using npm@1.0.105
-info using node@v0.4.12
+info using node@v0.6.10
+verbose /usr/bin/node node symlink
verbose config file /home/andy/.npmrc
-verbose config file /home/andy/local/node/etc/npmrc
+verbose config file /usr/etc/npmrc
verbose config file /usr/lib/node_modules/npm/npmrc
silly testEngine { name: 'basex',
-silly testEngine version: '0.4.2',
+silly testEngine version: '0.5.0',
silly testEngine description: 'A BaseX (XML database) client library',
silly testEngine keywords: [ 'xml', 'xquery', 'xslt', 'search', 'database' ],
silly testEngine author: { name: 'Andy Bunce' },
silly testEngine contributors: [ { name: 'Andy Bunce' } ],
silly testEngine main: './index.js',
-silly testEngine scripts: { test: 'vows test/vows.js' },
-silly testEngine repository:
-silly testEngine { type: 'git',
-silly testEngine url: 'git://github.com/apb2006/basex-node.git' },
-silly testEngine engines: { node: '>= 0.4 ' },
-silly testEngine _npmUser: { name: 'apb2006', email: 'bunce.andy@gmail.com' },
-silly testEngine _id: 'basex@0.4.2',
+silly testEngine scripts: { test: 'vows test/*.js' },
+silly testEngine repository: { type: 'git', url: 'git://github.com/apb2006/basex-node.git' },
silly testEngine dependencies: {},
-silly testEngine devDependencies: {},
+silly testEngine devDependencies: { vows: '0.6.x' },
+silly testEngine engines: { node: '>= 0.6 ' },
+silly testEngine license: 'BSD',
+silly testEngine _npmUser: { name: 'apb2006', email: 'bunce.andy@gmail.com' },
+silly testEngine _id: 'basex@0.5.0',
silly testEngine _engineSupported: true,
silly testEngine _npmVersion: '1.0.105',
-silly testEngine _nodeVersion: 'v0.4.12',
+silly testEngine _nodeVersion: 'v0.6.10',
silly testEngine _defaultsLoaded: true }
verbose caching /home/andy/workspace/basex-node/package.json
-verbose loadDefaults basex@0.4.2
+verbose loadDefaults basex@0.5.0
verbose run-script [ 'pretest', 'test', 'posttest' ]
-info pretest basex@0.4.2
-info test basex@0.4.2
+info pretest basex@0.5.0
+info test basex@0.5.0
verbose unsafe-perm in lifecycle true
-silly exec sh "-c" "vows test/vows.js"
+silly exec sh "-c" "vows test/*.js"
silly spawning [ 'sh',
-silly spawning [ '-c', 'vows test/vows.js' ],
+silly spawning [ '-c', 'vows test/*.js' ],
silly spawning '/home/andy/workspace/basex-node' ]
-info basex@0.4.2 Failed to exec test script
-ERR! basex@0.4.2 test: `vows test/vows.js`
-ERR! `sh "-c" "vows test/vows.js"` failed with 1
+info basex@0.5.0 Failed to exec test script
+ERR! basex@0.5.0 test: `vows test/*.js`
+ERR! `sh "-c" "vows test/*.js"` failed with 1
ERR!
-ERR! Failed at the basex@0.4.2 test script.
+ERR! Failed at the basex@0.5.0 test script.
ERR! This is most likely a problem with the basex package,
ERR! not with npm itself.
ERR! Tell the author that this fails on your system:
-ERR! vows test/vows.js
+ERR! vows test/*.js
ERR! You can get their info via:
ERR! npm owner ls basex
ERR! There is likely additional logging output above.
ERR!
-ERR! System Linux 2.6.35-30-generic
+ERR! System Linux 3.0.0-19-generic
ERR! command "node" "/usr/bin/npm" "test"
ERR! cwd /home/andy/workspace/basex-node
-ERR! node -v v0.4.12
+ERR! node -v v0.6.10
ERR! npm -v 1.0.105
ERR! code ELIFECYCLE
verbose exit [ 1, true ]
View
9 package.json
@@ -1,11 +1,14 @@
{ "name" : "basex",
- "version" : "0.4.2",
+ "version" : "0.5.0",
"description" : "A BaseX (XML database) client library",
"keywords": ["xml", "xquery", "xslt", "search", "database" ],
"author": "Andy Bunce ",
"contributors": [ "Andy Bunce" ],
"main": "./index.js",
- "scripts": { "test": "vows test/vows.js" },
+ "scripts": { "test": "vows test/*.js" },
"repository": { "type": "git","url": "git://github.com/apb2006/basex-node.git" },
- "engines": { "node": ">= 0.4 " }
+ "dependencies" : { },
+ "devDependencies": { "vows" : "0.6.x" },
+ "engines": { "node": ">= 0.6 " },
+ "license" : "BSD"
}
View
23 README.md → readme.md
@@ -3,15 +3,19 @@
This is BaseX client for node.js. It is work in progress.
-[BaseX](http://basex.org/) is an XML database. Built as a lightweight Java server, BaseX supports XPath, XQuery, and XSLT.
+[BaseX](http://basex.org/) is a very light-weight, high-performance and scalable
+ XML Database engine and XPath/XQuery 3.0 Processor,
+ including full support for the W3C Update and Full Text extensions.
+Built as a lightweight Java server, BaseX also supports XSLT, Webdav and RestXQ.
-It also boasts **full text search** and **update extensions**.
## Installing the BaseX Node client
-The best way to install this is with `npm` (available from [this repository](http://search.npmjs.org/#/basex)).
+To install with npm:
+
+npm install basex
```bash
$ mkdir project1;cd project1
@@ -22,7 +26,7 @@ The best way to install this is with `npm` (available from [this repository](htt
Once BaseX is running, test it.
```bash
- $ cd node_modules/basex/examples/
+ $ cd examples/
$ node Example.js
milliseconds: 0
{ result: '1 2 3 4 5 6 7 8 9 10',
@@ -34,12 +38,12 @@ Once BaseX is running, test it.
## Installing BaseX
1. Java is required
1. [Download](http://basex.org/products/download/all-downloads/) (http://basex.org/products/download/all-downloads/)
-(tested against BaseX version 7.0.2)
+(tested against BaseX version 7.2.1)
1. Run `basexserver &`
## Tests
-A test suite using [vows](http://vowsjs.org/) can be run.
+There is a test suite, using [vows](http://vowsjs.org/) .
```bash
vows test/* --spec
@@ -74,4 +78,9 @@ A test suite using [vows](http://vowsjs.org/) can be run.
# Inspiration
-Parts copied from [node_redis](https://github.com/mranney/node_redis)
+Parts inspired by [node_redis](https://github.com/mranney/node_redis)
+To install with npm:
+
+#license
+
+BSD license
View
6 test/vows.js → test/test-commands.js
@@ -1,4 +1,4 @@
-/* test interface using vows
+/* basex-node test interface using vows
*
* @TODO teardown connection
*/
@@ -85,7 +85,7 @@ vows.describe('BaseX interface test').addBatch({
var input = 'for $i in 1 to 10 return <xml>Text { $i }</xml>';
var query = session.query(input);
- query.iter(this.callback);
+ query.results(this.callback);
},
'we get no error' : function(err, reply) {
@@ -117,7 +117,7 @@ vows.describe('BaseX interface test').addBatch({
},
'and the result is an array' : function(err, reply) {
- assert.isArray(reply.result);
+ assert.isString(reply.result);
}
}
}).export(module); // Run it
View
51 test/test-parser.js
@@ -0,0 +1,51 @@
+/* basex-node test parser using vows
+ */
+var vows = require('vows')
+ , assert = require('assert')
+ ,parser = require('../lib/parser.js');
+
+//Create a Test Suite
+vows.describe('Parser test').addBatch({
+ 'read fld' : {
+ topic : {buffer:"abc\0\0"},
+
+ 'got a fld' : function(topic){
+ var r=parser.popmsg(topic,["test"])
+ assert.equal(r.test, "abc");
+ }
+ },
+ "after read":{
+ topic : {buffer:"abc\0\0"},
+
+ 'buffer changed' : function(topic){
+ var r=parser.popmsg(topic,["test"])
+ assert.equal(topic.buffer, "");
+ }
+ },
+ "status":{
+ topic : {buffer:"\0"},
+
+ 'got' : function(topic){
+ var r=parser.popmsg(topic,[])
+ assert.equal(r.ok, true);
+ }
+ },
+ "status wrong":{
+ topic : {buffer:"\2"},
+
+ 'error thrown' : function(topic){
+ assert.throws(function (){
+ var r=parser.popmsg(topic,[])
+ },"expected status marker")
+ }
+ }
+}).addBatch({
+ 'no status' : {
+ topic : {buffer:'a\0b\0'},
+
+ 'is parsed' : function(topic) {
+ var r=parser.popmsg(topic,["name","msg"],false);
+ assert.isObject(r);
+ }
+ }
+}).export(module); // Run it

0 comments on commit 403bbf9

Please sign in to comment.