Skip to content
Browse files

Initial creation of node-mdb repository

  • Loading branch information...
0 parents commit afb43ef09c6032f29ba1a1519b712f0792e52afa @robtweed committed May 5, 2011
Showing with 4,146 additions and 0 deletions.
  1. +174 −0 README.md
  2. +10 −0 gtm/mwire
  3. +4 −0 gtm/zmwire
  4. +1,774 −0 gtm/zmwire.m
  5. +2,184 −0 lib/mdb.js
174 README.md
@@ -0,0 +1,174 @@
+# node-mdb
+
+node-mdb is a re-implementation of M/DB, the Open Source clone of SimpleDB
+
+It has been completely re-written in Node.js Javascript and uses the free, Open Source GT.M database as the data repository.
+
+Rob Tweed <rtweed@mgateway.com>
+05 May 2011, M/Gateway Developments Ltd [http://www.mgateway.com](http://www.mgateway.com)
+
+Twitter@ @rtweed
+
+Google Group for discussions, support, advice etc: [http://groups.google.co.uk/group/mdb-community-forum](http://groups.google.co.uk/group/mdb-community-forum)
+
+See [http://www.mgateway.com/mdb.html](http://www.mgateway.com/mdb.html) for background on M/DB.
+
+## Important Note
+
+This is an early release of *node-mdb* and does not currently support the entire range of SimpleDB APIs. The APIs implemented so far are:
+
+- CreateDomain
+- DeleteDomain
+- ListDomains
+- DomainMetadata
+- PutAttributes
+- GetAttributes
+- DeleteAttributes
+
+Select can be used, but the only expression that is accepted at present is:
+
+ Select * from [yourDomainName]
+
+Future versions will include more of the SimpleDB APIs.
+
+The current version uses HTTP, but could be adapted for use with HTTPS
+
+## License
+
+Copyright (c) 2004-10 M/Gateway Developments Ltd,
+Reigate, Surrey UK.
+All rights reserved.
+
+http://www.mgateway.com
+Email: rtweed@mgateway.com
+
+This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+## Installing the GT.M Database
+
+This Free Open Source version of node-mdb is designed for use with the GT.M database [http://fisglobal.com/Products/TechnologyPlatforms/GTM/index.htm](http://fisglobal.com/Products/TechnologyPlatforms/GTM/index.htm).
+
+You can download GT.M from [http://sourceforge.net/projects/fis-gtm/](http://sourceforge.net/projects/fis-gtm/). Installation instructions are provided in the download kit.
+
+However, the quickest and easiest way to get a GT.M system going is to use Mike Clayton's *M/DB installer* for Ubuntu Linux which will create you a fully-working environment within a few minutes. Mike's installer actually installs the previous version of M/DB as well as GT.M, but the instructions later in this ReadMe document explain how to upgrade to the new Node.js-based version.
+
+Node.js can reside on the same server as GT.M or on a different server.
+
+The instructions below assume you'll be installing Node.js and GT.M on the same server.
+
+You can apply Mike's installer to a Ubuntu Linux system running on your own hardware, or running as a virtual machine. However, I find Amazon EC2 servers to be ideal for trying this kind of stuff out. I've tested it with Ubuntu 10.10.
+
+So, for example, to create an M/DB Appliance using Amazon EC2:
+
+- Start up a Ubuntu Lucid (10.10) instance, eg use ami-508c7839 for a 32-bit server version, or ami-548c783d for a 64-bit server version.
+
+**32-bit Ubuntu:**
+
+- Log in to your Ubuntu system and start a terminal session. If you've started a Ubuntu 10.4 or 10.10 EC2 AMI, log in with the username *ubuntu*
+
+ sudo apt-get update
+ cd /tmp
+ wget http://michaelgclayton.s3.amazonaws.com/mgwtools/mgwtools-1.11_i386.deb
+ sudo dpkg -i mgwtools-1.11_i386.deb (Ignore the errors that will be reported)
+ sudo apt-get -f install (and type y when asked)
+ rm mgwtools-1.11_i386.deb
+
+
+**64-bit Ubuntu:**
+
+- Log in to your Ubuntu system and start a terminal session. If you've started a Ubuntu 10.4 or 10.10 EC2 AMI, log in with the username *ubuntu*
+
+ sudo apt-get update
+ cd /tmp
+ wget http://michaelgclayton.s3.amazonaws.com/mgwtools/mgwtools-1.11_amd64.deb
+ sudo dpkg -i mgwtools-1.11_amd64.deb (Ignore the errors that will be reported)
+ sudo apt-get -f install (and type y when asked)
+ rm mgwtools-1.11_amd64.deb
+
+If you point a browser at the domain name/IP address assigned to the Ubuntu machine, you should now get the M/DB welcome screen. Follow the instructions you'll see in the welcome screen to initialise M/DB.
+
+You now have a fully-working and configured GT.M database, ready for use with node-mdb. Now follow the instructions below to upgrade to the new Node.js-based version of M/DB.
+
+## Installing Node.js
+
+Install Node.js and NPM on your server. See [http://nodejs.org](http://nodejs.org) for details.
+
+
+## Installing the node-mdb components
+
+- download the node-mdb repository, eg
+
+ git clone git://github.com/robtweed/node-mdb.git
+
+ The destination directory in which you'll find the files is determined by the path in which you ran the above command.
+
+ In the repository's */lib* directory, you'll find the main M/DB Node.js file: *mdb.js*. Copy this to the path */usr/local/gtm/ewd/* on your GT.M server (adjust this path appropriately if you've installed GT.M for use in another directory).
+
+In the repository's */gtm* directory, you'll find the M/Wire interface files that are used by mdb.js to access the GT.M database (See [https://github.com/robtweed/node-mwire](https://github.com/robtweed/node-mwire) for more details on the *node-mwire* used by *node-mdb*)
+
+You need to copy the files to the following directories:
+
+ - Copy the file *mwire* to */etc/xinetd.d/mwire*
+ - Copy the file *zmwire* to */usr/local/gtm/zmwire* and change its permissions to executable (eg 755)
+ - Copy the file *zmwire.m* to */usr/local/gtm/ewd/zmwire.m*
+
+ Next, you need to set up the port that is used by M/Wire to access GT.M. Edit */etc/services* and add the line:
+
+ mwire 6330/tcp # Service for M/Wire Protocol
+
+Nearly there! You just need to use NPM to install some other Node.js modules that are used by node-mdb.
+
+## Installing the additional Node.js modules uses by node-mdb
+
+Simply use NPM as follows:
+
+ npm install node-mwire
+ npm install redis-node
+ npm install node-uuid
+
+You're now ready to use *node-mdb*
+
+## Running node-mdb
+
+ cd /usr/local/gtm/ewd
+ node mdb.js
+
+Most SimpleDB clients will work with *node-mdb*. The main requirement is that you must be able to change the URL that normally points to the Amazon SimpleDB server, and point it at your M/DB instance instead, eg:
+
+ http://192.168.1.100:8081/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Background
+
+M/DB is an Open Source clone of SimpleDB that uses the Open Source GT.M Mumps database as the storage engine. M/DB behaves identically to SimpleDB, sharing its APIs.
+
+
+
+
+
+
+
+
+
10 gtm/mwire
@@ -0,0 +1,10 @@
+service mwire
+{
+ disable = no
+ type = UNLISTED
+ port = 6330
+ socket_type = stream
+ wait = no
+ user = root
+ server = /usr/local/gtm/zmwire
+}
4 gtm/zmwire
@@ -0,0 +1,4 @@
+#!/bin/bash
+cd /usr/local/gtm/ewd
+source /usr/local/gtm/gtmprofile
+mumps -r command^zmwire
1,774 gtm/zmwire.m
@@ -0,0 +1,1774 @@
+zmwire ; M/Wire Protocol for M Systems (eg GT.M, Cache)
+ ;
+ ; ----------------------------------------------------------------------------
+ ; | M/Wire |
+ ; | Copyright (c) 2004-11 M/Gateway Developments Ltd, |
+ ; | Reigate, Surrey UK. |
+ ; | All rights reserved. |
+ ; | |
+ ; | http://www.mgateway.com |
+ ; | Email: rtweed@mgateway.com |
+ ; | |
+ ; | This program is free software: you can redistribute it and/or modify |
+ ; | it under the terms of the GNU Affero General Public License as |
+ ; | published by the Free Software Foundation, either version 3 of the |
+ ; | License, or (at your option) any later version. |
+ ; | |
+ ; | This program is distributed in the hope that it will be useful, |
+ ; | but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ ; | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ ; | GNU Affero General Public License for more details. |
+ ; | |
+ ; | You should have received a copy of the GNU Affero General Public License |
+ ; | along with this program. If not, see <http://www.gnu.org/licenses/>. |
+ ; ----------------------------------------------------------------------------
+ ;
+ QUIT
+ ;
+ ; By default this server code runs on port 6330
+ ;
+ ; For GT.M systems it is invoked via xinetd:
+ ; Edit /etc/services and add the line:
+ ;
+ ; mwire 6330/tcp # Service for M/Wire Protocol
+ ;
+ ; Copy the file mwire to /etc/xinetd.d/mwire
+ ; Copy the file zmwire to /usr/local/gtm/zmwire and change
+ ; its permissions to executable (eg 755)
+ ;
+ ; These files may be edited to change the paths or port number
+ ; Restart xinetd using: sudo /etc/init.d/xinetd restart
+ ;
+ ; On GT.M systems you must also have installed MGWSI or m_apache
+ ; in order to provide the MD5 hashing function for passwords
+ ; Alternatively substitute the MD5 callout to the MD5 function of your choice
+ ;
+ ; For Cache systems, it is invoked via the M/Wire Daemon routine
+ ; which should be running as a jobbed process:
+ ;
+ ; job start^zmwireDaemon
+ ;
+ ; You can change the port number by simply editing the line
+ ;
+ ; port+1^zmwireDaemon
+ ;
+ ; Stop the Daemon process using ^RESJOB and restart it.
+ ;
+mwireVersion
+ ;;Build 15
+ ;
+mwireDate
+ ;;03 May 2011
+ ;
+version
+ ;
+ s output="+M/Wire "_$p($t(mwireVersion+1),";;",2,2000)_crlf
+ w output
+ QUIT
+ ;
+build
+ ;
+ n crlf,response
+ ;
+ s crlf=$c(13,10)
+ s response="*3"_crlf
+ w response
+ i $g(^zewd("trace"))=1 d trace("build: "_response_" sent")
+ ;
+ s response=$p($t(mwireVersion+1),";;",2,2000)
+ s response="$"_$l(response)_crlf_response_crlf
+ w response
+ i $g(^zewd("trace"))=1 d trace("build: "_response_" sent")
+ ;
+ s response=$p($t(mwireDate+1),";;",2,2000)
+ s response="$"_$l(response)_crlf_response_crlf
+ w response
+ i $g(^zewd("trace"))=1 d trace("build: "_response_" sent")
+ ;
+ s response=$zv
+ s response="$"_$l(response)_crlf_response_crlf
+ w response
+ i $g(^zewd("trace"))=1 d trace("build: "_response_" sent")
+ QUIT
+ ;
+command ;
+ n authNeeded,c,crlf,input,output
+ ;
+ d cleardown
+ i $zv["GT.M" s $zint="d monitoroutput"
+ s ^zmwire("connected",$j)=""
+ l +^zmwire("connected",$j)
+ s crlf=$c(13,10)
+ s authNeeded=0
+ s role="user"
+ i $d(^zmwire("auth")) s authNeeded=1
+loop
+ i $g(^zmwire("relink"))=1,'$d(^zmwire("relink","process",$j)) s ok=$$relinkRoutines()
+ r *c
+ i $c(c)="*" d
+ . s input=$$multiBulkRequest()
+ . i $g(^zewd("trace"))=1 d trace($j_": "_$h_": mwire input: "_input)
+ e d
+ . r input s input=$c(c)_input
+ . i $g(^zewd("trace"))=1 d trace($h_": mwire input: "_input)
+ . i input'="" d
+ . . i $zv["GT.M" s input=$e(input,1,$l(input)-2)
+ . . i $zv["Cache" s input=$e(input,1,$l(input)-1)
+ ;i input="PING" w "+PONG"_crlf g loop
+ i input="PING" s output="+PONG"_crlf w output g loop
+ i $d(^zmwire("monitor","listener")) d log(input)
+ i input="" g loop
+ i input="EXIT" g halt
+ i input="QUIT" g quit
+ i input="HALT" g halt
+ ;
+ i authNeeded,$e(input,1,4)'="AUTH" s output="-Authentication required"_crlf w output g loop
+ i 'authNeeded,$e(input,1,4)="AUTH" s output="-Authentication ignored"_crlf w output g loop
+ i $e(input,1,5)="AUTH " d auth($e(input,6,$l(input))) g loop
+ i 'authNeeded!(role="admin"),$e(input,1,12)="SETPASSWORD " d setpassword($e(input,13,$l(input))) g loop
+ ;
+ i $e(input,1,4)="SET " d set($e(input,5,$l(input))) g loop
+ i $e(input,1,10)="GETGLOBAL " d getGlobal($e(input,11,$l(input))) g loop
+ i $e(input,1,14)="GETJSONSTRING " d getJSON($e(input,15,$l(input))) g loop
+ i $e(input,1,14)="SETJSONSTRING " d setJSON($e(input,15,$l(input))) g loop
+ i $e(input,1,15)="RUNTRANSACTION " d runTransaction($e(input,16,$l(input))) g loop
+ i $e(input,1,4)="GET " d get($e(input,5,$l(input))) g loop
+ i $e(input,1,7)="INCRBY " d incrby($e(input,8,$l(input))) g loop
+ i $e(input,1,7)="DECRBY " d decrby($e(input,8,$l(input))) g loop
+ i $e(input,1,14)="NEXTSUBSCRIPT " d nextSubscript($e(input,15,$l(input)),1) g loop
+ i $e(input,1,18)="PREVIOUSSUBSCRIPT " d nextSubscript($e(input,19,$l(input)),-1) g loop
+ i $e(input,1,5)="KILL " d kill($e(input,6,$l(input))) g loop
+ i $e(input,1,4)="DEL " d kill($e(input,5,$l(input))) g loop
+ i $e(input,1,5)="DATA " d data($e(input,6,$l(input))) g loop
+ i $e(input,1,7)="EXISTS " d data($e(input,8,$l(input))) g loop
+ i $e(input,1,5)="INCR " d incr($e(input,6,$l(input))) g loop
+ i $e(input,1,5)="DECR " d decr($e(input,6,$l(input))) g loop
+ i $e(input,1,5)="LOCK " d lock($e(input,6,$l(input))) g loop
+ i $e(input,1,7)="UNLOCK " d unlock($e(input,8,$l(input))) g loop
+ i $e(input,1,6)="ORDER " d order($e(input,7,$l(input))) g loop
+ i $e(input,1,5)="NEXT " d order($e(input,6,$l(input))) g loop
+ i $e(input,1,9)="ORDERALL " d orderall($e(input,10,$l(input))) g loop
+ i $e(input,1,11)="GETGLOBALS2" d getGlobals() g loop
+ i $e(input,1,10)="GETGLOBALS" d getGlobalList() g loop
+ i $e(input,1,9)="MULTIGET " d multiGet($e(input,10,$l(input))) g loop
+ i $e(input,1,11)="GETALLSUBS " d orderall($e(input,12,$l(input))) g loop
+ i $e(input,1,14)="GETSUBSCRIPTS " d getAllSubscripts($e(input,15,$l(input))) g loop
+ i $e(input,1,9)="REVORDER " d reverseorder($e(input,10,$l(input))) g loop
+ i $e(input,1,9)="PREVIOUS " d reverseorder($e(input,10,$l(input))) g loop
+ i $e(input,1,6)="QUERY " d query($e(input,7,$l(input))) g loop
+ i $e(input,1,9)="QUERYGET " d queryget($e(input,10,$l(input))) g loop
+ i $e(input,1,10)="MERGEFROM " d mergefrom($e(input,11,$l(input))) g loop
+ i $e(input,1,11)="GETSUBTREE " d mergefrom($e(input,12,$l(input))) g loop
+ i $e(input,1,8)="MERGETO " d mergeto($e(input,9,$l(input))) g loop
+ i $e(input,1,11)="SETSUBTREE " d mergeto($e(input,12,$l(input))) g loop
+ i $e(input,1,9)="FUNCTION " d function($e(input,10,$l(input))) g loop
+ i $e(input,1,8)="EXECUTE " d function($e(input,9,$l(input))) g loop
+ i $e(input,1,6)="TSTART" d tstart g loop
+ i $e(input,1,7)="TCOMMIT" d tcommit g loop
+ i $e(input,1,9)="TROLLBACK" d trollback g loop
+ i $e(input,1,5)="MDATE" d mdate g loop
+ i $e(input,1,9)="PROCESSID" d processid g loop
+ i $e(input,1,7)="VERSION" d version g loop
+ i $e(input,1,8)="GETBUILD" d build g loop
+ i $e(input,1,8)="MVERSION" d zv g loop
+ i $e(input,1,4)="INFO" d info g loop
+ i $e(input,1,7)="MONITOR" d monitor g loop
+ s output="-"_input_" not recognized"_crlf w output
+ g loop
+ ;
+multiBulkRequest()
+ ;
+ n buff,c,i,input,j,len,noOfCommands,param,space
+ ;
+ s noOfCommands=""
+ f d q:c=13
+ . r *c q:c=13
+ . s noOfCommands=noOfCommands_$c(c)
+ r *x
+ ;
+ f i=1:1:noOfCommands d
+ . s len=""
+ . f d q:c=13
+ . . r *c
+ . . i $c(c)="$",len="" q
+ . . q:c=13
+ . . s len=len_$c(c)
+ . r *c
+ . s input=""
+ . i len>0 r input#len
+ . s param(i)=input
+ . r *c,*c
+ ;
+ s param(1)=$zconvert(param(1),"U")
+ ;QUIT "PING"
+ i param(1)="PING" QUIT param(1)
+ i param(1)="SET" QUIT param(1)_" "_param(2)_" "_$l(param(3))_crlf_param(3)
+ i param(1)="SETJSONSTRING" d QUIT param(1)_" "_param(2)_crlf_param(3)_crlf_param(4)
+ i param(1)="EXECUTE" d QUIT input
+ . i $e(param(3),1)="[" s input=param(1)_" "_param(2)_"("_$e(param(3),2,$l(param(3))-1)_")" q
+ . s input=param(1)_" "_param(2)
+ ;
+ s space="",input=""
+ f i=1:1:noOfCommands d
+ . s input=input_space_param(i)
+ . s space=" "
+ ;
+ QUIT input
+ ;
+halt
+ k ^zmwire("connected",$j)
+ HALT
+ ;
+quit
+ ;
+ i '$d(^zmwire("monitor","listener",$j)) g halt
+ k ^zmwire("monitor","listener",$j)
+ g loop
+ ;
+cleardown
+ ;
+ n ignore,pid
+ ;
+ s pid=""
+ f s pid=$o(^zmwire("connected",pid)) q:pid="" d
+ . i pid=$j q
+ . s ignore=1
+ . l +^zmwire("connected",pid):0 e s ignore=0
+ . i ignore d
+ . . l -^zmwire("connected",pid)
+ . . k ^zmwire("connected",pid)
+ . . k ^zmwire("monitor","listener",pid)
+ . . k ^zmwire("monitor","output",pid)
+ s pid=""
+ f s pid=$o(^zmwire("monitor","output",pid)) q:pid="" d
+ . i pid=$j q
+ . s ignore=1
+ . l +^zmwire("connected",pid):0 e s ignore=0
+ . l -^zmwire("connected",pid)
+ . i ignore d
+ . . k ^zmwire("monitor","output",pid)
+ QUIT
+ ;
+monitor
+ ;
+ i $zv'["GT.M" w "-Command unavailable"_crlf QUIT
+ n quit
+ ;
+ s ^zmwire("monitor","listener",$j)=""
+ s output="+OK"_crlf w output
+ f h 1 r quit:0 i $e(quit,1,4)="QUIT" q
+ k ^zmwire("monitor","listener",$j)
+ s output="+OK"_crlf w output
+ QUIT
+ ;
+log(input)
+ ;
+ i $zv'["GT.M" QUIT
+ ;
+ QUIT:'$d(^zmwire("monitor","listener"))
+ ;
+ n dev,inputr,io,lineNo,pid
+ ;
+ i input["AUTH" QUIT
+ i input["QUIT" QUIT
+ i input["EXIT" QUIT
+ i input["HALT" QUIT
+ s inputr=$re(input)
+ i $e(inputr,1,2)'=$c(10,13) s input=input_crlf
+ s pid=""
+ f s pid=$o(^zmwire("monitor","listener",pid)) q:pid="" d
+ . i pid=$j q
+ . s lineNo=$o(^zmwire("monitor","output",pid,""),-1)+1
+ . s ^zmwire("monitor","output",pid,lineNo)=input
+ . ;zsy "mupip intrpt "_pid_" >/dev/null"
+ . zsy "kill -USR1 "_pid
+ ;
+ QUIT
+ ;
+monitoroutput
+ ;
+ n lineNo
+ ;
+ s lineNo=""
+ f s lineNo=$o(^zmwire("monitor","output",$j,lineNo)) q:lineNo="" d
+ . w ^zmwire("monitor","output",$j,lineNo)
+ . k ^zmwire("monitor","output",$j,lineNo)
+ QUIT
+ ;
+info
+ ;
+ n count,ignore,pid,response
+ ;
+ s response="m_wire_version:"_$p($t(mwireVersion+1),";;",2,2000)_crlf
+ s pid="",count=0
+ f s pid=$o(^zmwire("connected",pid)) q:pid="" d
+ . s ignore=1
+ . i pid=$j d
+ . . s ignore=0
+ . e d
+ . . l +^zmwire("connected",pid):0 e s ignore=0
+ . i ignore d
+ . . l -^zmwire("connected",pid)
+ . . k ^zmwire("connected",pid)
+ . . k ^zmwire("monitor","listener",pid)
+ . e d
+ . . s count=count+1
+ s response=response_"connected_clients:"_count ;_crlf
+ s output="$"_$l(response)_crlf_response_crlf
+ w output
+ QUIT
+ ;
+auth(input)
+ ;
+ n pass
+ s pass=$$MD5(input)
+ i $d(^zmwire("auth",pass)) d
+ . s authNeeded=0
+ . s role=^zmwire("auth",pass)
+ . s output="+OK"_crlf
+ . w output
+ e d
+ . s output="-Invalid password"_crlf
+ . w output
+ QUIT
+ ;
+setpassword(input)
+ ;
+ ; SETPASSWORD secret
+ ; +OK <set as role=user>
+ ;
+ ; SETPASSWORD secret admin
+ ; +OK
+ ;
+ n pass,newrole
+ ;
+ i $d(^zmwire("auth")),role'="admin" s output="-Invalid command"_crlf w output QUIT
+ i $$stripSpaces(input)="" s output="-Invalid command"_crlf w output QUIT
+ s newrole="user"
+ i input[" " d
+ . s newrole=$p(input," ",2)
+ . s input=$p(input," ",1)
+ i '$d(^zmwire("auth")) s newrole="admin"
+ i newrole'="user",newrole'="admin" s output="-Invalid role"_crlf w output QUIT
+ ;
+ s pass=$$MD5(input)
+ s ^zmwire("auth",pass)=newrole
+ s output="+OK"_crlf
+ w output
+ QUIT
+ ;
+getGloRef(input)
+ ;
+ n gloName,gloRef,nb,subs
+ ;
+ s gloRef=input
+ i $e(gloRef,1)'="^" s gloRef="^"_gloRef
+ i $e(gloRef,$l(gloRef))="]" s gloRef=$e(gloRef,1,$l(gloRef)-1)
+ s gloName=$p(gloRef,"[",1)
+ s nb=$l(gloRef,"[")+2
+ s subs=$p(gloRef,"[",2,nb)
+ i subs="" QUIT gloName
+ QUIT gloName_"("_subs_")"
+ ;
+set(input)
+ ;
+ n data,gloName,gloRef,inputr,json,len,nb,nsp,ok,subs,x
+ ;
+ ; SET myglobal["1","xx yy",3] 5
+ ; hello
+ ; +OK
+ ; SET myGlo 5
+ ; hello
+ ; +OK
+ ;
+ i input[crlf d
+ . s data=$p(input,crlf,2,$l(input))
+ . s input=$p(input,crlf,1)
+ s nsp=$l(input," ")
+ s len=$p(input," ",nsp)
+ i len'=0,+len=0 s output="-Data length was not specified"_crlf w output QUIT
+ s gloRef=$p(input," ",1,nsp-1)
+ i $e(gloRef,1)'="^" s gloRef="^"_gloRef
+ i $e(gloRef,$l(gloRef))="]" s gloRef=$e(gloRef,1,$l(gloRef)-1)
+ s gloName=$p(gloRef,"[",1)
+ s nb=$l(gloRef,"[")+2
+ s subs=$p(gloRef,"[",2,nb)
+ s gloRef=gloName
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ i subs'="" s gloRef=gloRef_"("_subs_")"
+ i '$d(data) d
+ . s data=$$readChars(len)
+ . r ok
+ . i $d(^zmwire("monitor","listener")) d log(data)
+ i data["""" s data=$$replaceAll(data,"""","""""")
+ i data="zmwire_null_value" s data=""
+ s x="s "_gloRef_"="""_data_""""
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ s json="{""ok"":true}"
+ s output="$"_$l(json)_crlf_json_crlf
+ w output
+ i $g(^zewd("trace"))=1 d trace("set: ok:true sent")
+ QUIT
+ ;
+getGlobalList()
+ ;
+ n arrString,comma,count,glo,gloRef,list,response,x
+ ;
+ i $zv["GT.M" d
+ . s x="^%"
+ . i $d(@x) s list(x)=""
+ . f s x=$order(@x) q:x="" s list(x)=""
+ . ;
+ e d
+ . d getGlobalList^MDBMCache(.list)
+ ;
+ ;s count=0,glo=""
+ ;f s glo=$o(list(glo)) q:glo="" s count=count+1
+ ;s response="*"_count_crlf
+ ;w response
+ ;i $g(^zewd("trace"))=1 d trace("getGlobalList: "_response_" sent")
+ ;s glo=""
+ ;f s glo=$o(list(glo)) q:glo="" d
+ ;. s gloRef=$e(glo,2,$l(glo))
+ ;. s response="$"_$l(gloRef)_crlf_gloRef_crlf
+ ;. w response
+ ;. i $g(^zewd("trace"))=1 d trace("getGlobalList: "_response_" sent")
+ s arrString="["
+ s glo="",comma=""
+ f s glo=$o(list(glo)) q:glo="" d
+ . s gloRef=$e(glo,2,$l(glo))
+ . s arrString=arrString_comma_""""_gloRef_""""
+ . s comma=","
+ s arrString=arrString_"]"
+ s response="$"_$l(arrString)_crlf_arrString_crlf
+ w response
+ i $g(^zewd("trace"))=1 d trace("getGlobalList: "_response_" sent") ;
+ QUIT
+ ;
+getGlobals()
+ ;
+ n arrString,comma,count,glo,gloRef,list,response,x
+ ;
+ i $zv["GT.M" d
+ . s x="^%"
+ . i $d(@x) s list(x)=""
+ . f s x=$order(@x) q:x="" s list(x)=""
+ . ;
+ e d
+ . d getGlobalList^MDBMCache(.list)
+ ;
+ s count=0,glo=""
+ f s glo=$o(list(glo)) q:glo="" s count=count+1
+ s response="*"_count_crlf
+ w response
+ i $g(^zewd("trace"))=1 d trace("getGlobalList: "_response_" sent")
+ s glo=""
+ f s glo=$o(list(glo)) q:glo="" d
+ . s gloRef=$e(glo,2,$l(glo))
+ . s response="$"_$l(gloRef)_crlf_gloRef_crlf
+ . w response
+ . i $g(^zewd("trace"))=1 d trace("getGlobalList: "_response_" sent")
+ QUIT
+ ;
+get(input)
+ ;
+ n data,exists,gloRef,response,x
+ ;
+ ; GET myglobal["1","xx yy",3]
+ ; $6
+ ; foobar
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ s x="s exists=$d("_gloRef_")"
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ i exists'=1,exists'=11 s output="$-1"_crlf w output QUIT
+ s x="s data="_gloRef
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ ;
+ s response="$"_$l(data)_crlf_data_crlf
+ ;
+ w response
+ QUIT
+ ;
+getGlobal(input)
+ ;
+ n data,exists,gloRef,json,response,x
+ ;
+ ; GET myglobal["1","xx yy",3]
+ ; $6
+ ; foobar
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ s x="s exists=$d("_gloRef_"),"
+ ;x x
+ s x=x_"data=$g("_gloRef_")"
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ s json="{""value"":"""_data_""",""dataStatus"":"_exists_"}"
+ ;
+ s response="$"_$l(json)_crlf_json_crlf
+ ;
+ w response
+ i $g(^zewd("trace"))=1 d trace("getGlobal: response="_response)
+ QUIT
+ ;
+multiGet(input)
+ ;
+ n comma,dataStatus,error,exists,globalName,i,json,props,ref,response,subs,value
+ ;
+ ; MULTIGET myglobal[
+ ; {globalName:'xxx', subscripts:["x1","y1"]},
+ ; {globalName:'xxx', subscripts:["x2",""]},
+ ; {globalName:'xxx', subscripts:["x2","y2"]}
+ ;]
+ ;
+ ;
+ s error=$$parseJSON^%zewdJSON(input,.props,1)
+ i error'="" s output="-"_error_crlf w output QUIT
+ ;
+ s stop=0,error="",json="[",comma=""
+ f i=1:1 q:'$d(props(i)) d
+ . s dataStatus=0,value="",error=""
+ . s globalName=$g(props(i,"globalName"))
+ . i globalName="" s globalName=$g(props(i,"GlobalName"))
+ . i globalName="" d
+ . . s error="globalName not defined"
+ . e d
+ . . i $e(globalName,1)'="^" s globalName="^"_globalName
+ . . s ref="s exists=$d("_globalName
+ . . s subs=""
+ . . i $d(props(i,"subscripts")) d
+ . . . n comma,j,stop,sub
+ . . . s subs="(",comma="",stop=0
+ . . . f j=1:1 q:'$d(props(i,"subscripts",j)) d q:stop
+ . . . . s sub=props(i,"subscripts",j)
+ . . . . i sub="" s error="Subscript "_j_" is null",stop=1 q
+ . . . . s subs=subs_comma_""""_props(i,"subscripts",j)_"""",comma=","
+ . . . i error="" s subs=subs_")"
+ . . i error="" d
+ . . . s ref=ref_subs_")"
+ . . . s $zt=$$zt()
+ . . . x ref
+ . . . s $zt=""
+ . . . i exists d
+ . . . . s ref="s value=$g("_globalName
+ . . . . s ref=ref_subs_")"
+ . . . . s $zt=$$zt()
+ . . . . x ref
+ . . . . s $zt=""
+ . s json=json_comma_"{""value"":"""_value_""",""dataStatus"":"_exists_",""error"":"""_error_"""}"
+ . s comma=","
+ s json=json_"]"
+ ;
+ s response="$"_$l(json)_crlf_json_crlf
+ ;
+ w response
+ i $g(^zewd("trace"))=1 d trace("multiGet: response="_response)
+ QUIT
+ ;
+kill(input)
+ ;
+ n i,glo,gloRef,len,nsp,p1,p2,x
+ ;
+ ; KILL myglobal["1","xx yy",3]
+ ; +OK
+ ;
+ ;s nsp=$l(input," ")
+ ;f i=1:1:nsp d
+ ;. s glo=$p(input," ",i)
+ ;. q:glo=""
+ ;. s p1=$p(glo,"[",1)
+ ;. s p2=$p(glo,"[",2,2000)
+ ;. s p2=$e(p2,1,$l(p2)-1)
+ ;. s glo=p1_"("_p2_")"
+ ;. i glo["()" d
+ ;. . s len=$l(glo)
+ ;. . i $e(glo,len-1,len)="()" s glo=$e(glo,1,len-2)
+ ;. i glo'["zmwire" s glo(glo)=""
+ ;s glo=""
+ ;f s glo=$o(glo(glo)) q:glo="" d
+ ;. s x="k ^"_glo
+ . s $zt=$$zt()
+ ;. x x
+ ;. s $zt=""
+ ;
+ s glo=input
+ s p1=$p(glo,"[",1)
+ s p2=$p(glo,"[",2,2000)
+ s p2=$e(p2,1,$l(p2)-1)
+ s glo=p1_"("_p2_")"
+ i glo["()" d
+ . s len=$l(glo)
+ . i $e(glo,len-1,len)="()" s glo=$e(glo,1,len-2)
+ i glo'["zmwire" s glo(glo)=""
+
+ s x="k ^"_glo
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ s response="+ok"_crlf
+ i $g(^%zewd("trace"))=1 d trace("kill: response="_response)
+ w response
+ QUIT
+ ;
+data(input)
+ ;
+ n data,gloRef,x
+ ;
+ ; DATA myglobal["1","xx yy",3]
+ ; :10
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ s x="s data=$d("_gloRef_")"
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ i $g(^zewd("trace"))=1 d trace("input="_input_"; data="_data)
+ s output=":"_data_crlf
+ w output
+ QUIT
+ ;
+runTransaction(input)
+ ;
+ n error,globalName,i,json,props,ref,result,stop,subscripts
+ ;
+ s error=$$parseJSON^%zewdJSON(input,.props,1)
+ i error'="" s output="-"_error_crlf w output QUIT
+ ;
+ s stop=0,error=""
+ f i=1:1 q:'$d(props(i)) d q:stop
+ . s method=$g(props(i,"method"))
+ . i method="" s stop=1,error="Missing method in JSON transaction document at step "_i q
+ . i method="setJSON" d q:stop
+ . . n json
+ . . m json=props(i,"json")
+ . . i '$d(json) s stop=1,error="Missing JSON document in JSON transaction document at step "_i q
+ . . s globalName=$g(props(i,"globalName"))
+ . . i globalName="" s globalName=$g(props(i,"GlobalName"))
+ . . i globalName="" s stop=1,error="Missing Global name in JSON transaction document at step "_i q
+ . . i $e(globalName,1)'="^" s globalName="^"_globalName
+ . . s ref="m "_globalName
+ . . i $d(props(i,"subscripts")) d
+ . . . n comma,j
+ . . . s ref=ref_"(",comma=""
+ . . . f j=1:1 q:'$d(props(i,"subscripts",j)) d
+ . . . . s ref=ref_comma_""""_props(i,"subscripts",j)_"""",comma=","
+ . . . s ref=ref_")"
+ . . s ref=ref_"=json"
+ . . x ref
+ . i method="setGlobal" d q:stop
+ . . n value
+ . . s globalName=$g(props(i,"globalName"))
+ . . i globalName="" s globalName=$g(props(i,"GlobalName"))
+ . . i globalName="" s stop=1,error="Missing Global name in JSON transaction document at step "_i q
+ . . i $e(globalName,1)'="^" s globalName="^"_globalName
+ . . s ref="s "_globalName
+ . . i $d(props(i,"subscripts")) d
+ . . . n comma,j
+ . . . s ref=ref_"(",comma=""
+ . . . f j=1:1 q:'$d(props(i,"subscripts",j)) d
+ . . . . s ref=ref_comma_""""_props(i,"subscripts",j)_"""",comma=","
+ . . . s value=$g(props(i,"value"))
+ . . . i value="zmwire_null_value" s value=""
+ . . . s ref=ref_")="""_value_""""
+ . . x ref
+ . i method="kill" d q:stop
+ . . s globalName=$g(props(i,"globalName"))
+ . . i globalName="" s globalName=$g(props(i,"GlobalName"))
+ . . i globalName="" s stop=1,error="Missing Global name in JSON transaction document at step "_i q
+ . . i $e(globalName,1)'="^" s globalName="^"_globalName
+ . . s ref="k "_globalName
+ . . i $d(props(i,"subscripts")) d
+ . . . n comma,j
+ . . . s ref=ref_"(",comma=""
+ . . . f j=1:1 q:'$d(props(i,"subscripts",j)) d
+ . . . . s ref=ref_comma_""""_props(i,"subscripts",j)_"""",comma=","
+ . . . s ref=ref_")"
+ . . x ref
+ ;
+ i error'="" s output="-"_error_crlf w output QUIT
+ s response="+ok"_crlf
+ w response
+ i $g(^zewd("trace"))=1 d trace("transaction: response="_response)
+ QUIT
+ ;
+setJSON(input)
+ ;
+ n arr,del,error,flrc,inputr,gloRef,inputr,json,nb,nsp,props,ref,response,subs
+ ;
+ ; SETJSONSTRING myglobal["1","xx yy",3] CRLF {"a":123} CRLF 1
+ ; +ok
+ ;
+ s flrc=$c(10,13)
+ s gloRef=$p(input,crlf,1)
+ s input=$p(input,crlf,2,10000)
+ s inputr=$re(input)
+ s del=$p(inputr,flrc,1),del=$re(del)
+ s inputr=$p(inputr,flrc,2,10000) ; in case it contains crlfs
+ s json=$re(inputr)
+ i $zv["GT.M" d
+ . s json=$$unEscape(json)
+ e d
+ . s json=$$unEscape^MDBMCache(json)
+ ;
+ i $e(gloRef,1)'="^" s gloRef="^"_gloRef
+ i $e(gloRef,$l(gloRef))="]" s gloRef=$e(gloRef,1,$l(gloRef)-1)
+ s gloName=$p(gloRef,"[",1)
+ s nb=$l(gloRef,"[")+2
+ s subs=$p(gloRef,"[",2,nb)
+ s gloRef=gloName
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ i subs'="" s gloRef=gloRef_"("_subs_")"
+ s error=$$parseJSON^%zewdJSON(json,.props,1)
+ i error'="" s output="-Invalid JSON in setJSON: "_json_crlf w output QUIT
+ ;
+ s ref=""
+ i del s ref="k "_gloRef_" "
+ ;
+ s ref=ref_"m "_gloRef_"=props"
+ x ref
+ s response="+OK"_crlf
+ w response
+ i $g(^zewd("trace"))=1 d trace("setJSON: response="_response)
+ ;
+ QUIT
+ ;
+getJSON(input)
+ ;
+ n arr,inputr,gloRef,json,nb,nsp,ref,response,subs
+ ;
+ ; GETJSONSTRING myglobal["1","xx yy",3]
+ ; $5
+ ; {x:1}
+ ;
+ s gloRef=input
+ i $e(gloRef,1)'="^" s gloRef="^"_gloRef
+ i $e(gloRef,$l(gloRef))="]" s gloRef=$e(gloRef,1,$l(gloRef)-1)
+ s gloName=$p(gloRef,"[",1)
+ s nb=$l(gloRef,"[")+2
+ s subs=$p(gloRef,"[",2,nb)
+ s gloRef=gloName
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ i subs'="" s gloRef=gloRef_"("_subs_")"
+ s ref="m arr="_gloRef
+ x ref
+ i '$d(arr) d
+ . s response="$-1"_crlf
+ e d
+ . s json=$$arrayToJSON("arr")
+ . s response="$"_$l(json)_crlf_json_crlf
+ i $g(^zewd("trace"))=1 d trace("getJSON: response="_response)
+ ;
+ w response
+ ;
+ QUIT
+ ;
+incr(input)
+ ;
+ n data,gloRef,x
+ ;
+ ; INCR myglobal["1","xx yy",3]
+ ; :4
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ s x="s data=$increment("_gloRef_")"
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ s output=":"_data_crlf
+ w output
+ QUIT
+ ;
+incrby(input)
+ ;
+ n by,data,gloName,gloRef,inputr,len,nb,nsp,ok,subs,x
+ ;
+ ; INCRBY myglobal["1","xx yy",3] 3
+ ; :7
+ ;
+ s inputr=$re(input)
+ s by=$re($p(inputr," ",1))
+ s nsp=$l(input," ")+2
+ s gloRef=$p(inputr," ",2,nsp)
+ s gloRef=$re(gloRef)
+ i $e(gloRef,1)'="^" s gloRef="^"_gloRef
+ i $e(gloRef,$l(gloRef))="]" s gloRef=$e(gloRef,1,$l(gloRef)-1)
+ s gloName=$p(gloRef,"[",1)
+ s nb=$l(gloRef,"[")+2
+ s subs=$p(gloRef,"[",2,nb)
+ s gloRef=gloName
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ i subs'="" s gloRef=gloRef_"("_subs_")"
+ s x="s data=$increment("_gloRef_","_by_")"
+ s $zt=$$zt() x x
+ s $zt=""
+ s output=":"_data_crlf
+ w output
+ QUIT
+ ;
+function(input)
+ ;
+ n data,func,x
+ ;
+ ; FUNCTION label^rou("1","xx yy")
+ ; $5
+ ; hello
+ ;
+ s func=input
+ i func["^",$e(func,1,2)'="$$" s func="$$"_func
+ i func["class(",$e(func,1,2)'="##" s func="##"_func
+ s x="s data="_func
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ s output="$"_$l(data)_crlf_data_crlf
+ w output
+ QUIT
+ ;
+decr(input)
+ ;
+ n data,gloRef,x
+ ;
+ ; DECR myglobal["1","xx yy",3]
+ ; :3
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ s x="s data=$increment("_gloRef_",-1)"
+ s $zt=$$zt() x x
+ s $zt=""
+ s output=":"_data_crlf
+ w output
+ QUIT
+ ;
+decrby(input)
+ ;
+ n by,data,gloName,gloRef,inputr,nb,nsp,ok,subs,x
+ ;
+ ; DECRBY myglobal["1","xx yy",3] 3
+ ; :4
+ ;
+ s inputr=$re(input)
+ s by=$re($p(inputr," ",1))
+ s nsp=$l(input," ")+2
+ s gloRef=$p(inputr," ",2,nsp)
+ s gloRef=$re(gloRef)
+ i $e(gloRef,1)'="^" s gloRef="^"_gloRef
+ i $e(gloRef,$l(gloRef))="]" s gloRef=$e(gloRef,1,$l(gloRef)-1)
+ s gloName=$p(gloRef,"[",1)
+ s nb=$l(gloRef,"[")+2
+ s subs=$p(gloRef,"[",2,nb)
+ s gloRef=gloName
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ i subs'="" s gloRef=gloRef_"("_subs_")"
+ s x="s data=$increment("_gloRef_",-"_by_")"
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ s output=":"_data_crlf
+ w output
+ QUIT
+ ;
+nextSubscript(input,direction)
+ ;
+ n data,gloRef,response,subscript,x,value
+ ;
+ ; NEXTSUBSCRIPT myglobal["1","xx yy",""]
+ ; +abc
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ s x="s subscript=$o("_gloRef_",direction)"
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ s value="",data=0
+ i subscript'="" d
+ . s value=$g(^(subscript))
+ . s data=$d(^(subscript))
+ ;
+ s response="{""subscriptValue"":"""_subscript_""","
+ s response=response_"""dataStatus"":"_data_","
+ s response=response_"""dataValue"":"""_value_"""}"
+ ;
+ s response="$"_$l(response)_crlf_response_crlf
+ i $g(^zewd("trace"))=1 d trace("nextsubscript: response="_response)
+ w response
+ ;
+ QUIT
+ ;
+order(input)
+ ;
+ n data,gloRef,x
+ ;
+ ; ORDER myglobal["1","xx yy",""]
+ ; +abc
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ s x="s data=$o("_gloRef_")"
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ i data="" s output="$-1"_crlf w output QUIT
+ s output="$"_$l(data)_crlf_data_crlf
+ w output
+ QUIT
+ ;
+reverseorder(input)
+ ;
+ n data,gloRef,x
+ ;
+ ; REVORDER myglobal["1","xx yy",""]
+ ; +abc
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ s x="s data=$o("_gloRef_",-1)"
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ i data="" s output="$-1"_crlf w output QUIT
+ s output="$"_$l(data)_crlf_data_crlf
+ w output
+ QUIT
+ ;
+query(input)
+ ;
+ n data,gloRef,nb,p1,p2,x
+ ;
+ ; QUERY myglobal["1","xx yy"]
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ s x="s data=$q("_gloRef_")"
+ s $zt=$$zt()
+ x x
+ i data="" s output="$-1"_crlf w output QUIT
+ s data=$e(data,2,$l(data))
+ s p1=$p(data,"(",1)
+ s nb=$p(data,"(")+2
+ s p2=$p(data,"(",2,nb)
+ s p2=$e(p2,1,$l(p2)-1)
+ s data=p1_"["_p2_"]"
+ s output="$"_$l(data)_crlf_data_crlf
+ w output
+ s $zt=""
+ QUIT
+ ;
+queryget(input)
+ ;
+ n data,gloRef,nb,odata,p1,p2,value,x
+ ;
+ ; QUERYGET myglobal["1","xx yy"]
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ s x="s data=$q("_gloRef_")"
+ s $zt=$$zt()
+ x x
+ i data="" s output="$-1"_crlf w output QUIT
+ s odata=data
+ s output="*2"_crlf
+ w output
+ s data=$e(data,2,$l(data))
+ s p1=$p(data,"(",1)
+ s nb=$p(data,"(")+2
+ s p2=$p(data,"(",2,nb)
+ s p2=$e(p2,1,$l(p2)-1)
+ s data=p1_"["_p2_"]"
+ s output="$"_$l(data)_crlf_data_crlf
+ w output
+ s value=@odata
+ s output="$"_$l(value)_crlf_value_crlf
+ w output
+ s $zt=""
+ QUIT
+ ;
+lock(input)
+ ;
+ n gloName,gloRef,inputr,nb,nsp,ok,subs,time,x
+ ;
+ ; LOCK myglobal["1","xxyy"] 5
+ ; +OK
+ ;
+ s inputr=$re(input)
+ s time=$re($p(inputr," ",1))
+ i time?1N.N d
+ . s nsp=$l(input," ")+2
+ . s gloRef=$p(inputr," ",2,nsp)
+ e d
+ . s time=5
+ . s gloRef=inputr
+ s gloRef=$re(gloRef)
+ i $e(gloRef,1)'="^" s gloRef="^"_gloRef
+ i $e(gloRef,$l(gloRef))="]" s gloRef=$e(gloRef,1,$l(gloRef)-1)
+ s gloName=$p(gloRef,"[",1)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ s nb=$l(gloRef,"[")+2
+ s subs=$p(gloRef,"[",2,nb)
+ s gloRef=gloName
+ i subs'="" s gloRef=gloRef_"("_subs_")"
+ s x="s ok=1 l +"_gloRef_":"_time_" e s ok=0"
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ s output=":"_ok_crlf
+ w output
+ QUIT
+ ;
+unlock(input)
+ ;
+ n gloRef,x
+ ;
+ ; UNLOCK myglobal["1","xxyy"]
+ ; +OK
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ s x="l -"_gloRef
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ s output="+OK"_crlf
+ w output
+ QUIT
+ ;
+getAllSubscripts(input)
+ ;
+ n comma,data,exists,i,gloRef,len,rec,ref,subscripts,subs,subs1,x
+ ;
+ ; GETSUBSCRIPTS myglobal["1","xx yy"]
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ i $e(gloRef,$l(gloRef))=")" d
+ . s x="s exists=$d("_gloRef_")"
+ . s gloRef=$e(gloRef,1,$l(gloRef)-1)_","
+ e d
+ . s x="s exists=$d("_gloRef_")"
+ . s gloRef=gloRef_"("
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ i 'exists s output="$2"_crlf_"[]"_crlf w output QUIT
+ ;
+ s subs=""
+ s subs1=subs i subs1["""" s subs1=$$replaceAll(subs1,"""","""""")
+ s x="s subs=$o("_gloRef_""""_subs1_"""))"
+ x x
+ s len=3+$l(subs)
+ s comma=","
+ i subs'="" d
+ . f s subs=$o(^(subs)) q:subs="" d
+ . . s len=len+$l(comma)+2+$l(subs)
+ s len=len+1
+ s response="$"_len_crlf
+ w response
+ ;
+ s x="s subs=$o("_gloRef_""""_subs1_"""))"
+ x x
+ s response="["""_subs_""""
+ w response
+ i subs'="" d
+ . f s subs=$o(^(subs)) q:subs="" d
+ . . s response=comma_""""_subs_""""
+ . . w response
+ s response="]"_crlf
+ w response
+ ;
+ QUIT
+ ;
+orderall(input)
+ ;
+ n data,exists,i,gloRef,rec,subs,subs1,x
+ ;
+ ; ORDERALL myglobal["1","xx yy"]
+ ; *6
+ ; $2
+ ; aa
+ ; $5
+ ; hello
+ ; $2
+ ; bb
+ ; $5
+ ; world
+ ; $3
+ ; bba
+ ; $-1
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ i $e(gloRef,$l(gloRef))=")" d
+ . s x="s exists=$d("_gloRef_")"
+ . s gloRef=$e(gloRef,1,$l(gloRef)-1)_","
+ e d
+ . s x="s exists=$d("_gloRef_")"
+ . s gloRef=gloRef_"("
+ s $zt=$$zt()
+ x x
+ i 'exists s output="$-1"_crlf w output QUIT
+ ;
+ s subs="",rec=0
+ k ^CacheTempEWD($j)
+ f d q:subs=""
+ . s subs1=subs i subs1["""" s subs1=$$replaceAll(subs1,"""","""""")
+ . s x="s subs=$o("_gloRef_""""_subs1_"""))"
+ . x x
+ . i subs="" q
+ . s rec=rec+1
+ . s ^CacheTempEWD($j,rec)="$"_$l(subs)_crlf_subs_crlf
+ . s x="s exists=$d("_gloRef_""""_subs_"""))"
+ . x x
+ . i exists=1!(exists=11) d
+ . . s x="s data="_gloRef_""""_subs_""")"
+ . . x x
+ . . s rec=rec+1
+ . . s ^CacheTempEWD($j,rec)="$"_$l(data)_crlf_data_crlf
+ . e d
+ . . s rec=rec+1
+ . . s ^CacheTempEWD($j,rec)="$-1"_crlf
+ s $zt=""
+ s output="*"_rec_crlf
+ w output
+ f i=1:1:rec w ^CacheTempEWD($j,i)
+ k ^CacheTempEWD($j)
+ QUIT
+ ;
+mergefrom(input)
+ ;
+ n data,gloRef,i,params,resp,start,x
+ ;
+ ; MERGEFROM myglobal["1","a"]
+ ; *6
+ ; $1
+ ; 1 <keys>
+ ; $5
+ ; hello <data>
+ ; $9
+ ; 1,"a\"aa" <keys> note escaping
+ ; $5
+ ; world
+ ; $8
+ ; 2,"cccc"
+ ; $3
+ ; foo
+ ;
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ k ^CacheTempEWD($j)
+ s x="m ^CacheTempEWD($j)="_gloRef
+ s $zt=$$zt()
+ x x
+ s $zt=""
+ s x=$q(^CacheTempEWD($j,""))
+ i x="" d QUIT
+ . s output="*-1"_crlf
+ . w output
+ . k ^CacheTempEWD($j)
+ f i=1:1 s x=$q(@x) q:x=""
+ i $d(^CacheTempEWD($j))=1!($d(^CacheTempEWD($j))=11) s i=i+1
+ s output="*"_(i*2)_crlf
+ w output
+ i $d(^CacheTempEWD($j))=1!($d(^CacheTempEWD($j))=11) d
+ . s output="$-1"_crlf
+ . w output
+ . s output="$"_$l(^CacheTempEWD($j))_crlf_^CacheTempEWD($j)_crlf
+ . w output
+ s x=$q(^CacheTempEWD($j,""))
+ s start="^CacheTempEWD("_$j_","
+ s params=$p(x,start,2,2000)
+ s params=$e(params,1,$l(params)-1)
+ s resp=params
+ s output="$"_$l(resp)_crlf_resp_crlf
+ w output
+ s data=@x
+ s output="$"_$l(data)_crlf_data_crlf
+ w output
+ f i=1:1 s x=$q(@x) q:x="" d
+ . s params=$p(x,start,2,2000)
+ . s params=$e(params,1,$l(params)-1)
+ . s resp=params
+ . s output="$"_$l(resp)_crlf_resp_crlf
+ . w output
+ . s data=@x
+ . i data="" d
+ . . s output="$-1"_crlf
+ . . w output
+ . e d
+ . . s output="$"_$l(data)_crlf_data_crlf
+ . . w output
+ k ^CacheTempEWD($j)
+ QUIT
+ ;
+mergeto(input)
+ ;
+ n data,dataLength,error,gloRef,i,key,keyLength,noOfRecs,x
+ ;
+ ; MERGETO myglobal["1","a"]
+ ; *6
+ ; $1
+ ; 1 <keys>
+ ; $5
+ ; hello <data>
+ ; $7
+ ; 1,"aaa" <keys>
+ ; $5
+ ; world <data>
+ ; $8
+ ; 2,"cccc"
+ ; $3
+ ; foo
+ ; +OK
+ ; note $-1 for key length means no key - data put at top level
+ ;
+ s $zt=$$zt()
+ s gloRef=$$getGloRef(input)
+ i gloRef["^zmwire" s output="-No access allowed to this global"_crlf w output QUIT
+ i $e(gloRef,$l(gloRef))=")" s gloRef=$e(gloRef,1,$l(gloRef)-1)
+ ;
+ r noOfRecs
+ i $e(noOfRecs,1)'="*" s output="-Invalid: expected number of records"_crlf w output QUIT
+ s noOfRecs=+$e(noOfRecs,2,$l(noOfRecs))
+ i noOfRecs'?1N.N s output="-Invalid format for number of records"_crlf w output QUIT
+ i noOfRecs=0 QUIT "+OK"_crlf QUIT
+ i (noOfRecs#2)=1 s output="-Invalid: no of records must be an even number"_crlf w output QUIT
+ s noOfRecs=noOfRecs/2
+ k ^CacheTempEWD($j)
+ s error=""
+ k ^CacheTempEWD($j)
+ f i=1:1:noOfRecs d q:error'=""
+ . r keyLength
+ . i $e(keyLength,1)'="$" s error="Invalid record "_i_": record length" q
+ . s keyLength=+$e(keyLength,2,$l(keyLength))
+ . i keyLength=-1 d
+ . . s key=""
+ . e d
+ . . i keyLength'?1N.N s error="Invalid record "_i_": bad format for record length" q
+ . . i keyLength=0 s error="Invalid record "_i_": record length cannot be zero" q
+ . . s key=$$readChars(keyLength)
+ . . r ok d log(key)
+ . . ;r key#keyLength,ok d log(key)
+ . . i key["\""" s key=$$replaceAll(key,"\""","""""")
+ . i error'="" q
+ . r dataLength
+ . i $e(dataLength,1)'="$" s error="Invalid record "_i_": expected data length" q
+ . s dataLength=+$e(dataLength,2,$l(dataLength))
+ . i dataLength'=-1,dataLength'?1N.N s error="Invalid record "_i_": bad format for data length" q
+ . i dataLength=-1 d
+ . . s data=""
+ . e d
+ . . s data=$$readChars(dataLength)
+ . . r ok
+ . . d log(data)
+ . . ;r data#dataLength,ok d log(data)
+ . . i data["""" s data=$$replaceAll(data,"""","""""")
+ . i key="" d
+ . . n gloRef1
+ . . s gloRef1=gloRef
+ . . i gloRef["(" s gloRef1=gloRef1_")"
+ . . s x="s "_gloRef1_"="""_data_""""
+ . e d
+ . . n gloRef1
+ . . s gloRef1=gloRef
+ . . i gloRef'["(" d
+ . . . s gloRef1=gloRef1_"("
+ . . e d
+ . . . s gloRef1=gloRef1_","
+ . . s x="s "_gloRef1_key_")="""_data_""""
+ . s ^CacheTempEWD($j,i)=x
+ i error'="" s output="-"_error_crlf w output QUIT
+ f i=1:1:noOfRecs d
+ . s x=^CacheTempEWD($j,i)
+ . x x
+ k ^CacheTempEWD($j)
+ s output="+OK"_crlf
+ w output
+ s $zt=""
+ QUIT
+ ;
+mdate
+ ;
+ n date,day,time
+ ;
+ s date=$h
+ s day=+date
+ s output="*2"_crlf_"$"_$l(day)_crlf_day_crlf
+ w output
+ s time=$p(date,",",2)
+ s output="$"_$l(time)_crlf_time_crlf
+ w output
+ QUIT
+ ;
+tstart
+ s $zt=$$zt()
+ TSTART
+ s output="+OK"_crlf
+ w output
+ s $zt=""
+ QUIT
+ ;
+tcommit
+ s $zt=$$zt()
+ TCOMMIT
+ s output="+OK"_crlf
+ w output
+ s $zt=""
+ QUIT
+ ;
+trollback
+ s $zt=$$zt()
+ TROLLBACK
+ s output="+OK"_crlf
+ w output
+ s $zt=""
+ QUIT
+ ;
+zv
+ s output="+"_$zv_crlf
+ w output
+ QUIT
+ ;
+zt()
+ i $zv["GT.M" QUIT "g executeError^zmwire"
+ QUIT "executeError^zmwire"
+ ;
+processid
+ s output=":"_$j_crlf
+ w output
+ QUIT
+ ;
+ping
+ s output="+PONG"_crlf
+ w output
+ QUIT
+ ;
+executeError
+ s output="-Invalid Command"_crlf
+ w output
+ g loop
+ ;
+replaceAll(InText,FromStr,ToStr) ; Replace all occurrences of a substring
+ ;
+ n p
+ ;
+ s p=InText
+ i ToStr[FromStr d QUIT p
+ . n i,stop,tempText,tempTo
+ . s stop=0
+ . f i=0:1:255 d q:stop
+ . . q:InText[$c(i)
+ . . q:FromStr[$c(i)
+ . . q:ToStr[$c(i)
+ . . s stop=1
+ . s tempTo=$c(i)
+ . s tempText=$$replaceAll(InText,FromStr,tempTo)
+ . s p=$$replaceAll(tempText,tempTo,ToStr)
+ f q:p'[FromStr S p=$$replace(p,FromStr,ToStr)
+ QUIT p
+ ;
+replace(InText,FromStr,ToStr) ; replace old with new in string
+ ;
+ n np,p1,p2
+ ;
+ i InText'[FromStr q InText
+ s np=$l(InText,FromStr)+1
+ s p1=$p(InText,FromStr,1),p2=$p(InText,FromStr,2,np)
+ QUIT p1_ToStr_p2
+ ;
+readChars(length)
+ ;
+ n data,i,x
+ ;
+ s data=""
+ f i=1:1:length r *x s data=data_$c(x)
+ QUIT data
+ ;
+stripSpaces(string)
+ i $zv["Cache" QUIT $$stripSpaces^MDBMCache(string)
+ ;
+ s string=$$stripLeadingSpaces(string)
+ QUIT $$stripTrailingSpaces(string)
+ ;
+stripLeadingSpaces(string)
+ ;
+ n i
+ ;
+ f i=1:1:$l(string) QUIT:$e(string,i)'=" "
+ QUIT $e(string,i,$l(string))
+ ;
+stripTrailingSpaces(string)
+ ;
+ n i,spaces,new
+ ;
+ s spaces=$$makeString(" ",100)
+ s new=string_spaces
+ QUIT $p(new,spaces,1)
+ ;
+makeString(char,len) ; create a string of len characters
+ ;
+ n str
+ ;
+ s str="",$p(str,char,len+1)=""
+ QUIT str
+ ;
+MD5(string)
+ ;
+ ; n hash
+ ;
+ i $zv["Cache" QUIT $$MD5^MDBMCache(string)
+ ;
+ QUIT $$MD5^%ZMGWSIS(string,1,1)
+ ;
+unEscape(string)
+ ;
+ n buf,outstring,p1,p2,hex,asc
+ ;
+ s buf=string
+ s outstring=""
+ f q:buf'["%" d
+ . s p1=$p(buf,"%",1)
+ . s outstring=outstring_p1
+ . s p2=$p(buf,"%",2,50000)
+ . i $e(p2)="u" s buf=$e(p2,6,9999),hex=$e(p2,2,5),outstring=outstring_$c($$hex2Ascii(hex)-1264) q
+ . s hex=$e(p2,1,2)
+ . s buf=$e(p2,3,$l(p2))
+ . s asc=$$hex2Ascii(hex)
+ . s outstring=outstring_$c(asc)
+ QUIT (outstring_buf)
+ ;
+hex2Ascii(string)
+ ;
+ n asc,c,conv,err,i,n,power
+ ;
+ s string=$zconvert(string,"U")
+ s asc=0
+ f i=0:1:9 S conv(i)=i
+ s conv("A")=10
+ s conv("B")=11
+ s conv("C")=12
+ s conv("D")=13
+ s conv("E")=14
+ s conv("F")=15
+ s n=-1,err=0
+ f i=$l(string):-1:1 d q:err
+ . s n=n+1
+ . s power=16**n
+ . s c=$e(string,i)
+ . i '$d(conv(c)) s err=1 q
+ . s asc=asc+(conv(c)*power)
+ i err QUIT "-1"
+ QUIT asc
+ ;
+ts()
+ s last=$g(^zmwire("lastts"))
+ n io,p,resp
+ s io=$io
+ s p="time"
+ o p:(COMMAND="date +%s%N":READONLY)::"PIPE"
+ u p
+ r resp q:$ZEOF
+ c p
+ u io
+ s ^zmwire("lastts")=resp
+ QUIT ((resp-last)/1000000000)
+ ;
+relinkRoutines()
+ n list,rou,xrou
+ i $g(^zewd("trace"))=1 d trace("Process "_$j_": Relinking...")
+ s rou=""
+ f s rou=$view("RTNNEXT",rou) q:rou="" d
+ . i rou="zmwire" q
+ . i rou="%zewdGTMRuntime" q
+ . i rou="%zewdPHP" q
+ . i rou="MDB" q
+ . i rou="ewdWLewdmgrrelink" q
+ . i rou="%ZMGWSI" q
+ . i rou="%ZMGWSIS" q
+ . i rou="GTM$DMOD" q
+ . s xrou=rou
+ . i $e(xrou,1)="%" s xrou="_"_$e(xrou,2,$l(xrou))
+ . zl xrou
+ . i $g(^zewd("trace"))=1 d trace("relinked "_rou)
+ s ^zmwire("relink","process",$j)=""
+ i $g(^zewd("trace"))=1 d trace("Process "_$j_": Relinking complete")
+ QUIT ""
+ ;
+relink ;
+ s ^zmwire("relink")=1
+ k ^zmwire("relink","process")
+ QUIT
+ ;
+arrayToJSON(name)
+ n subscripts
+ i '$d(@name) QUIT "[]"
+ QUIT $$walkArray("",name)
+ ;
+walkArray(json,name,subscripts)
+ ;
+ n allNumeric,arrComma,brace,comma,count,cr,dd,i,no,numsub,dblquot,quot
+ n ref,sub,subNo,subscripts1,type,valquot,value,xref,zobj
+ ;
+ s cr=$c(13,10),comma=","
+ s (dblquot,valquot)=""""
+ s dd=$d(@name)
+ i dd=1!(dd=11) d i dd=1 QUIT json
+ . s value=@name
+ . i value'[">" q
+ . s json=$$walkArray(json,value,.subscripts)
+ s ref=name_"("
+ s no=$o(subscripts(""),-1)
+ i no>0 f i=1:1:no d
+ . s quot=""""
+ . i subscripts(i)?."-"1N.N s quot=""
+ . s ref=ref_quot_subscripts(i)_quot_","
+ s ref=ref_"sub)"
+ s sub="",numsub=0,subNo=0,count=0
+ s allNumeric=1
+ f s sub=$o(@ref) q:sub="" d q:'allNumeric
+ . i sub'?1N.N s allNumeric=0
+ . s count=count+1
+ . i sub'=count s allNumeric=0
+ ;i allNumeric,count=1 s allNumeric=0
+ s allNumeric=0
+ i allNumeric d
+ . s json=json_"["
+ e d
+ . s json=json_"{"
+ s sub=""
+ f s sub=$o(@ref) q:sub="" d
+ . s subscripts(no+1)=sub
+ . s subNo=subNo+1
+ . s dd=$d(@ref)
+ . i dd=1 d
+ . . s value=@ref
+ . . i value["/" s value=$$replaceAll(value,"\","\\")
+ . . s value=$$removeControlChars(value)
+ . . i 'allNumeric d
+ . . . i sub["\" s sub=$$replaceAll(sub,"\","\\")
+ . . . s sub=$$removeControlChars(sub)
+ . . . s json=json_""""_sub_""":"
+ . . s type="literal"
+ . . i $$numeric(value) s type="numeric"
+ . . i value="true"!(value="false") s type="boolean"
+ . . i $e(value,1)="{",$e(value,$l(value))="}" s type="variable"
+ . . i type="literal" s value=valquot_value_valquot
+ . . d
+ . . . s json=json_value_","
+ . k subscripts1
+ . m subscripts1=subscripts
+ . i dd>9 d
+ . . ;i sub?1N.N d
+ . . ;. i subNo=1 d
+ . . ;. . s numsub=1
+ . . ;. . s json=$e(json,1,$l(json)-1)_"["
+ . . ;e d
+ . . ;. s json=json_""""_sub_""":"
+ . . i sub["\" s sub=$$replaceAll(sub,"\","\\")
+ . . s sub=$$removeControlChars(sub)
+ . . s json=json_""""_sub_""":"
+ . . s json=$$walkArray(json,name,.subscripts1)
+ . . d
+ . . . s json=json_","
+ ;
+ s json=$e(json,1,$l(json)-1)
+ i allNumeric d
+ . s json=json_"]"
+ e d
+ . s json=json_"}"
+ QUIT json ; exit!
+ ;
+trace(text,clear) ; trace ;
+ n i
+ s text=$g(text)
+ i $g(clear)=1 k ^%zewdTrace
+ s i=$increment(^%zewdTrace)
+ s ^%zewdTrace(i)=text
+ QUIT
+ ;
+parseJSON(jsonString,propertiesArray,mode)
+ ;
+ n array,arrRef,buff,c,error
+ ;
+ k propertiesArray
+ s error=""
+ s buff=$g(jsonString)
+ s buff=$$replaceAll(buff,"\""","\'")
+ s arrRef="array"
+ s c=$e(buff,1)
+ s buff=$e(buff,2,$l(buff))
+ d
+ . i c="{" d q
+ . . n prefix
+ . . s prefix="""zobj1"""
+ . . i $g(mode)=1 s prefix=""
+ . . s error=$$parseJSONObject(.buff,prefix)
+ . . q:error
+ . . i buff'="" s error=1
+ . i c="[" d q
+ . . n prefix
+ . . s prefix=1
+ . . i $g(mode)=1 s prefix=""
+ . . s error=$$parseJSONArray(.buff,prefix)
+ . . q:error
+ . . i buff'="" s error=1
+ . s error=1
+ i error=1 QUIT "Invalid JSON"
+ m propertiesArray=array
+ QUIT ""
+ ;
+parseJSONObject(buff,subs)
+ n c,error,name,stop,subs2,value,x
+ s stop=0,name="",error=""
+ f d q:stop
+ . s c=$e(buff,1)
+ . i c="" s error=1,stop=1 q
+ . s buff=$e(buff,2,$l(buff))
+ . i c="[" s error=1,stop=1 q
+ . i c="}" d q
+ . . s stop=1
+ . i c=":" d q
+ . . n subs2
+ . . s value=$$getJSONValue(.buff)
+ . . d q:stop
+ . . . i value="" q
+ . . . i $e(value,1)="""",$e(value,$l(value))="""" q
+ . . . i value="true"!(value="false") s value=""""_value_"""" q
+ . . . i $$numeric(value) q
+ . . . s error=1,stop=1
+ . . i value="",$e(buff,1)="{" d q
+ . . . i $e(name,1)'="""",$e(name,$l(name))'="""" s name=""""_name_""""
+ . . . s subs2=subs
+ . . . i subs'="" s subs2=subs2_","
+ . . . s subs2=subs2_name
+ . . . i $g(mode)="" s subs2=subs2_",""zobj1"""
+ . . . s buff=$e(buff,2,$l(buff))
+ . . . s error=$$parseJSONObject(.buff,subs2)
+ . . . i error=1 s stop=1 q
+ . . i value="",$e(buff,1)="[" d q
+ . . . ;s subs2=subs_","""_name_""",""1"""
+ . . . i $e(name,1)'="""",$e(name,$l(name))'="""" s name=""""_name_""""
+ . . . s subs2=subs
+ . . . i subs'="" s subs2=subs2_","
+ . . . s subs2=subs2_name
+ . . . s buff=$e(buff,2,$l(buff))
+ . . . s error=$$parseJSONArray(.buff,subs2)
+ . . . i error=1 s stop=1 q
+ . . i $e(name,1)="""",$e(name,$l(name))'="""" s error=1,stop=1 q
+ . . i $e(name,1)'="""",$e(name,$l(name))="""" s error=1,stop=1 q
+ . . i $e(name,1)'="""",$e(name,$l(name))'="""" s name=""""_name_""""
+ . . s subs2=subs
+ . . i subs'="" s subs2=subs2_","
+ . . s subs2=subs2_name
+ . . i value["\'" s value=$$replaceAll(value,"\'","""""")
+ . . s x="s "_arrRef_"("_subs2_")="_value
+ . . x x
+ . i c="," s name="" q
+ . s name=name_c q
+ QUIT error
+ ;
+parseJSONArray(buff,subs)
+ n c,error,name,no,stop,subs2,value,x
+ s stop=0,name="",no=0,error=""
+ f d q:stop
+ . s c=$e(buff,1)
+ . i c="" s error=1,stop=1 q
+ . s buff=$e(buff,2,$l(buff))
+ . i c=":" s error=1,stop=1 q
+ . i c="]" d q
+ . . s stop=1
+ . . i name="" q
+ . . s no=no+1
+ . . s subs2=subs
+ . . i subs'="" s subs2=subs2_","
+ . . s subs2=subs2_no
+ . . s x="s "_arrRef_"("_subs2_")="_name
+ . . x x
+ . i c="[" d q
+ . . s no=no+1
+ . . s subs2=subs
+ . . i subs'="" s subs2=subs2_","
+ . . s subs2=subs2_no
+ . . ;s buff=$e(buff,2,$l(buff))
+ . . s error=$$parseJSONArray(.buff,subs2)
+ . . i error=1 s stop=1 q
+ . i c="{" d q
+ . . s no=no+1
+ . . s subs2=subs
+ . . i subs'="" s subs2=subs2_","
+ . . s subs2=subs2_no
+ . . i $g(mode)="" s subs2=subs2_",""zobj1"""
+ . . ;s buff=$e(buff,2,$l(buff))
+ . . s error=$$parseJSONObject(.buff,subs2)
+ . . i error=1 s stop=1 q
+ . s subs2=subs
+ . i subs'="" s subs2=subs2_","
+ . s subs2=subs2_""""_name_""""
+ . i c="," d q
+ . . i name="" q
+ . . d q:stop
+ . . . i $e(name,1)="""",$e(name,$l(name))="""" q
+ . . . ;i value="true"!(value="false") s value=""""_value_"""" q
+ . . . i $$numeric(name) q
+ . . . s error=1,stop=1
+ . . s no=no+1
+ . . s subs2=subs
+ . . i subs'="" s subs2=subs2_","
+ . . s subs2=subs2_""""_no_""""
+ . . s x="s "_arrRef_"("_subs2_")="_name
+ . . x x
+ . . s name=""
+ . s name=name_c q
+ QUIT error
+ ;
+getJSONValue(buff)
+ n c,isLiteral,lc,stop,value
+ s stop=0,value="",isLiteral=0,lc=""
+ f d q:stop q:buff=""
+ . s c=$e(buff,1)
+ . i value="",c="""" s isLiteral=1
+ . i 'isLiteral,c="[" s stop=1 q
+ . i 'isLiteral,c="{" s stop=1 q
+ . i c="}" d q:stop
+ . . i isLiteral,lc'="""" q
+ . . s stop=1
+ . i c="," d q:stop
+ . . i isLiteral,lc'="""" q
+ . . s stop=1
+ . s buff=$e(buff,2,$l(buff))
+ . s value=value_c
+ . s lc=c
+ QUIT value
+ ;
+numeric(value)
+ i $e(value,1)=0,$l(value)>1 QUIT 0
+ i value?1N.N QUIT 1
+ i value?1"-"1N.N QUIT 1
+ i value?1N.N1"."1N.N QUIT 1
+ i value?1"-"1N.N1"."1N.N QUIT 1
+ i value?1"."1N.N QUIT 1
+ i value?1"-."1N.N QUIT 1
+ QUIT 0
+ ;
+removeControlChars(string)
+ n c,i,newString
+ s newString=""
+ f i=1:1:$l(string) d
+ . s c=$e(string,i)
+ . i $a(c)<32 s c="~"
+ . s newString=newString_c
+ QUIT newString
+ ;
+
+
2,184 lib/mdb.js
@@ -0,0 +1,2184 @@
+/*
+
+ ----------------------------------------------------------------------------
+ | Node.js version of M/DB: An emulation of SimpleDB |
+ | |
+ | Copyright (c) 2011 M/Gateway Developments Ltd, |
+ | Reigate, Surrey UK. |
+ | All rights reserved. |
+ | |
+ | http://www.mgateway.com |
+ | Email: rtweed@mgateway.com |
+ | |
+ | This program is free software: you can redistribute it and/or modify |
+ | it under the terms of the GNU Affero General Public License as |
+ | published by the Free Software Foundation, either version 3 of the |
+ | License, or (at your option) any later version. |
+ | |
+ | This program is distributed in the hope that it will be useful, |
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ | GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public License |
+ | along with this program. If not, see <http://www.gnu.org/licenses/>. |
+ ----------------------------------------------------------------------------
+
+ Modify the parameters below if required:
+
+ - httpPort: the port on which Node listens for incoming HTTP requests
+
+ - trace: true|false - controls how much is reported to the console.log
+ If set false, just BoxUsage is reported for each request
+ - sdbURLPattern: the URL location, as per standard SimpleDB requests
+
+ - mdbURLPattern: for users of earlier, non-Node.js versions of M/DB, to ensure
+ compatibility with clients configured for M/DB
+
+ - database: currently just GT.M, in which case ensure the M/Wire parameters
+ are correctly defined for your GT.M environment. The usual defaults
+ are used below.
+
+ - silentStart: turns off all logging to console.log.
+
+****************************************************************
+*/
+
+
+var httpPort = 8081;
+var trace = false;
+
+var sdbURLPattern = '/';
+var mdbURLPattern = '/mdb/request.mgwsi';
+
+var database = 'gtm';
+var mwire = {};
+var db;
+if (database === 'gtm') {
+ var mwire = {
+ port: 6330,
+ host: '127.0.0.1',
+ poolSize: 4
+ };
+ var mwireLib = require("node-mwire");
+ db = new mwireLib.Client({port: mwire.port, host: mwire.host, poolSize: mwire.poolSize});
+}
+var silentStart = false;
+
+/*
+***************** Set up environment ********************
+*/
+
+var http = require("http");
+var url = require("url");
+var queryString = require("querystring");
+var path = require("path");
+var uuid = require("node-uuid");
+var crypto = require("crypto");
+
+var i;
+var accessKey = {};
+var MDB = {
+ indexLength:180
+};
+
+/*
+******************* Web Server *********************************
+*/
+
+http.createServer(function(request, response) {
+
+ request.content = '';
+
+ request.on("data", function(chunk) {
+ request.content += chunk;
+ });
+
+ request.on("end", function(){
+ //console.log("request = " + request.content);
+ //console.log("headers: " + JSON.stringify(request.headers));
+ var SDB = {startTime: new Date().getTime(),
+ request: request,
+ response: response
+ };
+ var urlObj = url.parse(request.url, true);
+ if (request.method === 'POST') {
+ SDB.nvps = parseContent(request.content);
+ }
+ else {
+ SDB.nvps = urlObj.query;
+ }
+ //console.log("nvps = " + JSON.stringify(nvps));
+ //console.log("queryString: " + JSON.stringify(urlObj.query));
+ var uri = urlObj.pathname;
+ if ((uri.indexOf(sdbURLPattern) !== -1)||(uri.indexOf(mdbURLPattern) !== -1)) {
+ //appears to be an SDB (M/DB) request
+ processSDBRequest(SDB);
+ }
+ else {
+ //console.log("Bad M/DB URL Pattern");
+ var uriString = 'http://' + request.headers.host + request.url;
+ var error = {code:'InvalidURI', message: 'The URI ' + uriString + ' is not valid',status:400};
+ returnError(SDB ,error);
+ }
+ });
+
+}).listen(httpPort);
+
+var processSDBRequest = function(SDB) {
+ var accessKeyId = SDB.nvps.AWSAccessKeyId;
+ // check security credentials are present and fetch secretKey if not already available
+ if (!accessKeyId) {
+ if (trace) console.log("missing Access Key Id");
+ var error = {code:'AuthMissingFailure', message: 'AWS was not able to authenticate the request: access credentials are missing',status:403};
+ returnError(SDB, error);
+ }
+ else {
+ if (accessKey[accessKeyId]) {
+ //console.log("secret key already locally held: " + accessKey[SDB.nvps.AWSAccessKeyId]);
+ // Commence signature validation process
+ validateSDBRequest(SDB, accessKey[accessKeyId]);
+ }
+ else {
+ if (database === 'gtm') {
+ db.clientPool[db.connection()].getGlobal('MDBUAF', ['keys', accessKeyId],
+ function (error, results) {
+ if (!error) {
+ if (results.value !== '') {
+ accessKey[accessKeyId] = results.value;
+ if (trace) console.log("secret key fetched: " + results.value);
+ // Commence signature validation process
+ validateSDBRequest(SDB, results.value);
+ }
+ else {
+ if (trace) console.log("missing Access Key Id");
+ var error = {code:'AuthMissingFailure', message: 'AWS was not able to authenticate the request: access credentials are missing',status:403};
+ returnError(SDB, error);
+ }
+ }
+ }
+ );
+ }
+ }
+ }
+};
+
+/*
+*********** Request Signature Security Validation ****************
+*/
+
+var validateSDBRequest = function(SDB, secretKey) {
+ //check validity of request signature, based on security credentials
+ var nvps = SDB.nvps;
+ var type;
+ var stringToSign = createStringToSign(SDB);
+ if (stringToSign === -1) {
+ if (trace) console.log("invalid signature version");
+ var error = {code:'InvalidParameterValue', message: 'Value (' + nvps.SignatureVersion + ') for parameter SignatureVersion is invalid. Valid Signature versions are 0, 1 and 2',status:400};
+ returnError(SDB, error);
+ }
+ else {
+ if (trace) console.log("StringToSign = " + stringToSign);
+ switch (nvps.SignatureMethod) {
+ case 'HmacSHA1':
+ type = 'sha1';
+ break;
+ case 'HmacSHA256':
+ type = 'sha256';
+ break;
+ case 'HmacSHA512':
+ type = 'sha512';
+ break;
+ default:
+ type = 'sha1';
+ }
+ var hash = digest(stringToSign, secretKey, type);
+ if (trace) console.log("hash = " + hash + "; Signature = " + nvps.Signature);
+ if (hash === nvps.Signature) {
+ processSDBAction(SDB);
+ }
+ else {
+ if (trace) console.log("Signatures don't match");
+ errorResponse('SignatureDoesNotMatch', SDB)
+ }
+ }
+};
+
+/*
+*********** Action Selection/Processing *********************
+*/
+
+var processSDBAction = function(SDB) {
+ var nvps = SDB.nvps;
+ var action = nvps.Action;
+ if (!action) {
+ if (trace) console.log("missing action");
+ errorResponse('MissingAction', SDB)
+ }
+ else {
+
+ if (!nvps.Version) SDB.nvps.Version = '2009-04-15';
+ if (trace) console.log("Action = " + action);
+
+ switch (action) {
+ case "ListDomains":
+ ListDomains(SDB);
+ break;
+
+ case "CreateDomain":
+ CreateDomain(SDB);
+ break;
+
+ case "DeleteDomain":
+ DeleteDomain(SDB);
+ break;
+
+ case "DomainMetadata":
+ DomainMetadata(SDB);
+ break;
+
+ case "GetAttributes":
+ GetAttributes(SDB);
+ break;
+
+ case "PutAttributes":
+ PutAttributes(SDB);
+ break;
+
+ case "DeleteAttributes":
+ DeleteAttributes(SDB);
+ break;
+
+ case "Select":
+ Select(SDB);
+ break;
+
+ default:
+ // error unrecognised command
+ if (trace) console.log("unrecognised Action");
+ var error = {code:'InvalidAction', message: 'The action ' + action + ' is not valid for this web service',status:400};
+ returnError(SDB, error);
+ }
+ }
+};
+
+/*
+ ***************** Actions ***************************
+*/
+
+/*
+ ***************** Select ***************************
+
+ ** Note! The Select implementation is incomplete at present.
+
+ The only expressions that works are:
+
+ Select * from [domainName]
+ Select * from [domainName] Limit [n]
+
+*/
+
+var Select = function(SDB) {
+
+ var error = {code:'InvalidQueryExpression', message: 'The specified query expression syntax is not valid.',status:400};
+ var expression = SDB.nvps.SelectExpression;
+ //console.log("Select Expression = " + expression);
+ var piece = expression.split(' ');
+ if (piece[0].toLowerCase() !== 'select') {
+ returnError(SDB, error);
+ return;
+ }
+ if (piece[1] !== '*') {
+ returnError(SDB, error);
+ return;
+ }
+ if (piece[2].toLowerCase() !== 'from') {
+ returnError(SDB, error);
+ return;
+ }
+ if (typeof piece[3] === 'undefined') {
+ returnError(SDB, error);
+ return;
+ }
+ var domainName = piece[3];
+ var limit = 0;
+
+ if (typeof piece[4] !== 'undefined') {
+ if (piece[4].toLowerCase() === 'limit') {
+ limit = parseInt(piece[5]);
+ }
+ if (piece[4].toLowerCase() === 'where') {
+ // not yet supported!
+ returnError(SDB, error);
+ return;
+ }
+ }
+ SDB.nvps.DomainName = domainName;
+ SDB.select = {
+ items: piece[1],
+ condition: '',
+ limit: limit
+ };
+
+ var invoke = {
+ found: function(domainIndex, SDB) {
+ select(domainIndex, SDB);
+ },
+ notFound: function(SDB) {
+ errorResponse('NoSuchDomain', SDB);
+ }
+ };
+ checkDomainExists(invoke, SDB);
+};
+
+var select = function(domainIndex, SDB) {
+ SDB.select.domainIndex = domainIndex;
+ var accessKeyId = SDB.nvps.AWSAccessKeyId;
+ if (trace) console.log("in select - domainIndex = " + domainIndex);
+ if (trace) console.log("SDB.select = " + JSON.stringify(SDB.select));
+ if ((SDB.select.items === '*')&&(SDB.select.condition === '')) {
+ SDB.select.xml = {};
+ db.clientPool[db.connection()].getAllSubscripts('MDB', [accessKeyId, 'domains', domainIndex, 'items'], function (error, results) {
+ if (trace) console.log("itemIndex array = " + JSON.stringify(results));
+ SDB.select.items = results;
+ if (results.length > 0) {
+ // now get attribs Names
+ db.clientPool[db.connection()].getJSON('MDB', [accessKeyId, 'domains', domainIndex, 'attribs'], function (error, results) {
+ if (trace) console.log("attribs = " + JSON.stringify(results));
+ SDB.select.attribs = results;
+ SDB.select.count = 0;
+ for (var i = 0; i < SDB.select.items.length; i++) {
+ var itemIndex = SDB.select.items[i];
+ getItemDetails(itemIndex, SDB);
+ }
+ });
+ }
+ else {
+ SelectResponse(SDB);
+ }
+ });
+ }
+ else {
+ var error = {code:'InvalidQueryExpression', message: 'The specified query expression syntax is not valid.',status:400};
+ returnError(SDB, error);
+ }
+};
+
+var getItemDetails = function(itemIndex, SDB) {
+ var accessKeyId = SDB.nvps.AWSAccessKeyId;
+ var domainIndex = SDB.select.domainIndex;
+ //console.log("get details for itemIndex = " + itemIndex);
+ db.clientPool[db.connection()].getJSON('MDB', [accessKeyId, 'domains', domainIndex, 'items', itemIndex], function (error, results) {
+ //console.log("item contents = " + JSON.stringify(results));
+ var attribs = results.attribs;
+ var attribNames = SDB.select.attribs;
+ var itemName = attribs['0'].value['1'];
+ var attribIndex;
+ var attrib;
+ var values;
+ var valueIndex;
+ var attribName;
+ var attribValue;
+ var xml = '<Item><Name>' + itemName + '</Name>';
+ for (attribIndex in attribs) {
+ if (attribIndex !== '0') {
+ attribName = attribNames[attribIndex];
+ attrib = attribs[attribIndex];
+ if (typeof attrib.value !== 'undefined') {
+ values = attrib.value;
+ for (valueIndex in values) {
+ attribValue = xmlEscape(values[valueIndex]);
+ xml = xml + '<Attribute><Name>' + attribName + '</Name><Value>' + attribValue + '</Value></Attribute>';
+ }
+ }
+ }
+ }
+ xml = xml + '</Item>';
+ SDB.select.xml[itemIndex] = xml;
+ SDB.select.count++;
+ if (SDB.select.count === SDB.select.items.length) {
+ SelectResponse(SDB);
+ }
+ });
+};
+
+var SelectResponse = function(SDB) {
+ var nvps = SDB.nvps;
+ var action = nvps.Action;
+ var selectXML = SDB.select.xml;
+ var xml;
+ var itemIndex;
+ //console.log("xml = " + JSON.stringify(selectXML));
+ if (SDB.select.items.length === 0) {
+ xml = responseStart({action: action, version: nvps.Version, hasContent: false, isEmpty: true});
+ }
+ else {