merge from trunk to javascript branch
diff --git a/examples/messenger/c/CMakeLists.txt b/examples/messenger/c/CMakeLists.txt
index 4f40924..1b32d0c 100644
--- a/examples/messenger/c/CMakeLists.txt
+++ b/examples/messenger/c/CMakeLists.txt
@@ -21,8 +21,12 @@
add_executable(recv recv.c)
add_executable(send send.c)
+add_executable(recv-async recv-async.c)
+add_executable(send-async send-async.c)
include_directories(${Proton_INCLUDE_DIRS})
target_link_libraries(recv ${Proton_LIBRARIES})
target_link_libraries(send ${Proton_LIBRARIES})
+target_link_libraries(recv-async ${Proton_LIBRARIES})
+target_link_libraries(send-async ${Proton_LIBRARIES})
diff --git a/examples/messenger/c/recv-async.c b/examples/messenger/c/recv-async.c
new file mode 100644
index 0000000..1f49166
--- /dev/null
+++ b/examples/messenger/c/recv-async.c
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// This is a re-implementation of recv.c using non-blocking/asynchronous calls.
+
+#include "proton/message.h"
+#include "proton/messenger.h"
+
+#include "pncompat/misc_funcs.inc"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#if EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
+pn_message_t * message;
+pn_messenger_t * messenger;
+
+#define check(messenger) \
+ { \
+ if(pn_messenger_errno(messenger)) \
+ { \
+ die(__FILE__, __LINE__, pn_error_text(pn_messenger_error(messenger))); \
+ } \
+ } \
+
+void die(const char *file, int line, const char *message)
+{
+ fprintf(stderr, "%s:%i: %s\n", file, line, message);
+ exit(1);
+}
+
+void usage(void)
+{
+ printf("Usage: recv [options] <addr>\n");
+ printf("-c \tPath to the certificate file.\n");
+ printf("-k \tPath to the private key file.\n");
+ printf("-p \tPassword for the private key.\n");
+ printf("<addr>\tAn address.\n");
+ exit(0);
+}
+
+void process(void) {
+ while(pn_messenger_incoming(messenger))
+ {
+ pn_messenger_get(messenger, message);
+ check(messenger);
+
+ {
+ pn_tracker_t tracker = pn_messenger_incoming_tracker(messenger);
+ char buffer[1024];
+ size_t buffsize = sizeof(buffer);
+ const char* subject = pn_message_get_subject(message);
+ pn_data_t* body = pn_message_body(message);
+ pn_data_format(body, buffer, &buffsize);
+
+ printf("Address: %s\n", pn_message_get_address(message));
+ printf("Subject: %s\n", subject ? subject : "(no subject)");
+ printf("Content: %s\n", buffer);
+
+ pn_messenger_accept(messenger, tracker, 0);
+ }
+ }
+}
+
+#if EMSCRIPTEN // For emscripten C/C++ to JavaScript compiler.
+void pump(int fd, void* userData) {
+ while (pn_messenger_work(messenger, 0) >= 0) {
+ process();
+ }
+}
+
+void onclose(int fd, void* userData) {
+ process();
+}
+
+void onerror(int fd, int errno, const char* msg, void* userData) {
+ printf("error callback fd = %d, errno = %d, msg = %s\n", fd, errno, msg);
+}
+#endif
+
+int main(int argc, char** argv)
+{
+ char* certificate = NULL;
+ char* privatekey = NULL;
+ char* password = NULL;
+ char* address = (char *) "amqp://~0.0.0.0";
+ int c;
+
+ message = pn_message();
+ messenger = pn_messenger(NULL);
+ pn_messenger_set_blocking(messenger, false); // Needs to be set non-blocking to behave asynchronously.
+
+ opterr = 0;
+
+ while((c = getopt(argc, argv, "hc:k:p:")) != -1)
+ {
+ switch(c)
+ {
+ case 'h':
+ usage();
+ break;
+
+ case 'c': certificate = optarg; break;
+ case 'k': privatekey = optarg; break;
+ case 'p': password = optarg; break;
+
+ case '?':
+ if (optopt == 'c' ||
+ optopt == 'k' ||
+ optopt == 'p')
+ {
+ fprintf(stderr, "Option -%c requires an argument.\n", optopt);
+ }
+ else if(isprint(optopt))
+ {
+ fprintf(stderr, "Unknown option `-%c'.\n", optopt);
+ }
+ else
+ {
+ fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
+ }
+ return 1;
+ default:
+ abort();
+ }
+ }
+
+ if (optind < argc)
+ {
+ address = argv[optind];
+ }
+
+ /* load the various command line options if they're set */
+ if(certificate)
+ {
+ pn_messenger_set_certificate(messenger, certificate);
+ }
+
+ if(privatekey)
+ {
+ pn_messenger_set_private_key(messenger, privatekey);
+ }
+
+ if(password)
+ {
+ pn_messenger_set_password(messenger, password);
+ }
+
+ pn_messenger_start(messenger);
+ check(messenger);
+
+ pn_messenger_subscribe(messenger, address);
+ check(messenger);
+
+ pn_messenger_recv(messenger, -1); // Set to receive as many messages as messenger can buffer.
+
+#if EMSCRIPTEN // For emscripten C/C++ to JavaScript compiler.
+ emscripten_set_socket_error_callback(NULL, onerror);
+
+ emscripten_set_socket_open_callback(NULL, pump);
+ emscripten_set_socket_connection_callback(NULL, pump);
+ emscripten_set_socket_message_callback(NULL, pump);
+ emscripten_set_socket_close_callback(NULL, onclose);
+#else // For native compiler.
+ while (1) {
+ pn_messenger_work(messenger, -1); // Block indefinitely until there has been socket activity.
+ process();
+ }
+#endif
+
+ return 0;
+}
+
diff --git a/examples/messenger/c/send-async.c b/examples/messenger/c/send-async.c
new file mode 100644
index 0000000..2c76e6c
--- /dev/null
+++ b/examples/messenger/c/send-async.c
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// This is a re-implementation of send.c using non-blocking/asynchronous calls.
+
+#include "proton/message.h"
+#include "proton/messenger.h"
+#include "proton/driver.h"
+
+#include "pncompat/misc_funcs.inc"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#if EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
+pn_message_t * message;
+pn_messenger_t * messenger;
+pn_tracker_t tracker;
+int running = 1;
+
+#define check(messenger) \
+ { \
+ if(pn_messenger_errno(messenger)) \
+ { \
+ die(__FILE__, __LINE__, pn_error_text(pn_messenger_error(messenger))); \
+ } \
+ } \
+
+void die(const char *file, int line, const char *message)
+{
+ fprintf(stderr, "%s:%i: %s\n", file, line, message);
+ exit(1);
+}
+
+void usage(void)
+{
+ printf("Usage: send [-a addr] [message]\n");
+ printf("-a \tThe target address [amqp[s]://domain[/name]]\n");
+ printf("message\tA text string to send.\n");
+ exit(0);
+}
+
+void process(void) {
+ pn_status_t status = pn_messenger_status(messenger, tracker);
+ if (status != PN_STATUS_PENDING) {
+ if (running) {
+ pn_messenger_stop(messenger);
+ running = 0;
+ }
+ }
+
+ if (pn_messenger_stopped(messenger)) {
+ pn_message_free(message);
+ pn_messenger_free(messenger);
+ }
+}
+
+#if EMSCRIPTEN // For emscripten C/C++ to JavaScript compiler.
+void pump(int fd, void* userData) {
+ while (pn_messenger_work(messenger, 0) >= 0) {
+ process();
+ }
+}
+
+void onclose(int fd, void* userData) {
+ process();
+}
+
+void onerror(int fd, int errno, const char* msg, void* userData) {
+ printf("error callback fd = %d, errno = %d, msg = %s\n", fd, errno, msg);
+}
+#endif
+
+int main(int argc, char** argv)
+{
+ int c;
+ opterr = 0;
+ char * address = (char *) "amqp://0.0.0.0";
+ char * msgtext = (char *) "Hello World!";
+
+ while((c = getopt(argc, argv, "ha:b:c:")) != -1)
+ {
+ switch(c)
+ {
+ case 'a': address = optarg; break;
+ case 'h': usage(); break;
+
+ case '?':
+ if(optopt == 'a')
+ {
+ fprintf(stderr, "Option -%c requires an argument.\n", optopt);
+ }
+ else if(isprint(optopt))
+ {
+ fprintf(stderr, "Unknown option `-%c'.\n", optopt);
+ }
+ else
+ {
+ fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
+ }
+ return 1;
+ default:
+ abort();
+ }
+ }
+
+ if (optind < argc) msgtext = argv[optind];
+
+ message = pn_message();
+ messenger = pn_messenger(NULL);
+ pn_messenger_set_blocking(messenger, false); // Needs to be set non-blocking to behave asynchronously.
+ pn_messenger_set_outgoing_window(messenger, 1024);
+
+ pn_messenger_start(messenger);
+
+ pn_message_set_address(message, address);
+ pn_data_t* body = pn_message_body(message);
+ pn_data_put_string(body, pn_bytes(strlen(msgtext), msgtext));
+
+ pn_messenger_put(messenger, message);
+ check(messenger);
+
+ tracker = pn_messenger_outgoing_tracker(messenger);
+
+#if EMSCRIPTEN // For emscripten C/C++ to JavaScript compiler.
+ emscripten_set_socket_error_callback(NULL, onerror);
+
+ emscripten_set_socket_open_callback(NULL, pump);
+ emscripten_set_socket_connection_callback(NULL, pump);
+ emscripten_set_socket_message_callback(NULL, pump);
+ emscripten_set_socket_close_callback(NULL, onclose);
+#else // For native compiler.
+ while (running) {
+ pn_messenger_work(messenger, -1); // Block indefinitely until there has been socket activity.
+ process();
+ }
+
+ while (!pn_messenger_stopped(messenger)) {
+ pn_messenger_work(messenger, 0);
+ process();
+ }
+#endif
+
+ return 0;
+}
+
diff --git a/examples/messenger/javascript/client.js b/examples/messenger/javascript/client.js
new file mode 100644
index 0000000..62f9a61
--- /dev/null
+++ b/examples/messenger/javascript/client.js
@@ -0,0 +1,104 @@
+#!/usr/bin/env node
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Simple client for use with server.js illustrating request/response
+
+// Check if the environment is Node.js and if not log an error and exit.
+if (typeof process === 'object' && typeof require === 'function') {
+ var proton = require("qpid-proton");
+
+ var address = "amqp://0.0.0.0";
+ var subject = "UK.WEATHER";
+ var replyTo = "~/replies";
+ var msgtext = "Hello World!";
+ var tracker = null;
+ var running = true;
+
+ var message = new proton.Message();
+ var messenger = new proton.Messenger();
+
+ var pumpData = function() {
+ while (messenger.incoming()) {
+ var t = messenger.get(message);
+
+ console.log("Reply");
+ console.log("Address: " + message.getAddress());
+ console.log("Subject: " + message.getSubject());
+
+ // body is the body as a native JavaScript Object, useful for most real cases.
+ //console.log("Content: " + message.body);
+
+ // data is the body as a proton.Data Object, used in this case because
+ // format() returns exactly the same representation as recv.c
+ console.log("Content: " + message.data.format());
+
+ messenger.accept(t);
+ messenger.stop();
+ }
+
+ if (messenger.isStopped()) {
+ message.free();
+ messenger.free();
+ }
+ };
+
+ var args = process.argv.slice(2);
+ if (args.length > 0) {
+ if (args[0] === '-h' || args[0] === '--help') {
+ console.log("Usage: node client.js [-r replyTo] [-s subject] <addr> (default " + address + ")");
+ console.log("Options:");
+ console.log(" -r <reply to> The message replyTo (default " + replyTo + ")");
+ console.log(" -s <subject> The message subject (default " + subject + ")");
+ process.exit(0);
+ }
+
+ for (var i = 0; i < args.length; i++) {
+ var arg = args[i];
+ if (arg.charAt(0) === '-') {
+ i++;
+ var val = args[i];
+ if (arg === '-r') {
+ replyTo = val;
+ } else if (arg === '-s') {
+ subject = val;
+ }
+ } else {
+ address = arg;
+ }
+ }
+ }
+
+ messenger.on('error', function(error) {console.log(error);});
+ messenger.on('work', pumpData);
+ messenger.setOutgoingWindow(1024);
+ messenger.recv(); // Receive as many messages as messenger can buffer.
+ messenger.start();
+
+ message.setAddress(address);
+ message.setSubject(subject);
+ message.setReplyTo(replyTo);
+ message.body = msgtext;
+
+ tracker = messenger.put(message);
+} else {
+ console.error("client.js should be run in Node.js");
+}
+
diff --git a/examples/messenger/javascript/drain.js b/examples/messenger/javascript/drain.js
new file mode 100644
index 0000000..04ced73
--- /dev/null
+++ b/examples/messenger/javascript/drain.js
@@ -0,0 +1,70 @@
+#!/usr/bin/env node
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Check if the environment is Node.js and if not log an error and exit.
+if (typeof process === 'object' && typeof require === 'function') {
+ var proton = require("qpid-proton");
+
+ console.log("drain not implemented yet");
+ process.exit(0);
+
+ var address = "amqp://~0.0.0.0";
+ var message = new proton.Message();
+ var messenger = new proton.Messenger();
+
+ var pumpData = function() {
+ while (messenger.incoming()) {
+ var t = messenger.get(message);
+
+ console.log("Address: " + message.getAddress());
+ console.log("Subject: " + message.getSubject());
+
+ // body is the body as a native JavaScript Object, useful for most real cases.
+ //console.log("Content: " + message.body);
+
+ // data is the body as a proton.Data Object, used in this case because
+ // format() returns exactly the same representation as recv.c
+ console.log("Content: " + message.data.format());
+
+ messenger.accept(t);
+ }
+ };
+
+ var args = process.argv.slice(2);
+ if (args.length > 0) {
+ if (args[0] === '-h' || args[0] === '--help') {
+ console.log("Usage: recv <addr> (default " + address + ").");
+ process.exit(0);
+ }
+
+ address = args[0];
+ }
+
+ messenger.on('error', function(error) {console.log(error);});
+ messenger.on('work', pumpData);
+ messenger.recv(); // Receive as many messages as messenger can buffer.
+ messenger.start();
+
+ messenger.subscribe(address);
+} else {
+ console.error("drain.js should be run in Node.js");
+}
+
diff --git a/examples/messenger/javascript/proxy.js b/examples/messenger/javascript/proxy.js
new file mode 100755
index 0000000..cac5cf5
--- /dev/null
+++ b/examples/messenger/javascript/proxy.js
@@ -0,0 +1,105 @@
+#!/usr/bin/env node
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * proxy.js is a simple node.js command line application that uses the ws2tcp.js
+ * library to proxy from a WebSocket to a TCP Socket or vice versa.
+ * <p>
+ * Usage: node proxy.js [options]
+ * Options:");
+ * -p <listen port>, --port <listen port> (default 5673 for ws2tcp
+ * 5672 for tcp2ws)
+ * -t <target port>, --tport <target port> (default listen port - 1 for ws2tcp
+ * listen port + 1 for tcp2ws)
+ * -h <target host>, --thost <target host> (default 0.0.0.0)
+ * -m <ws2tcp or tcp2ws>, --method <ws2tcp or tcp2ws> (default ws2tcp)
+ * @Author Fraser Adams
+ * @file
+ */
+
+// Check if the environment is Node.js and if not log an error and exit.
+if (typeof process === 'object' && typeof require === 'function') {
+ var proxy = require('./ws2tcp.js');
+
+ var lport = 5673;
+ var tport = lport - 1;
+ var thost = '0.0.0.0';
+ var method = 'ws2tcp';
+
+ var args = process.argv.slice(2);
+ if (args.length > 0) {
+ if (args[0] === '-h' || args[0] === '--help') {
+ console.log("Usage: node proxy.js [options]");
+ console.log("Options:");
+ console.log(" -p <listen port>, --port <listen port> (default " + lport + " for ws2tcp");
+ console.log(" " + tport + " for tcp2ws)");
+ console.log(" -t <target port>, --tport <target port> (default listen port - 1 for ws2tcp");
+ console.log(" listen port + 1 for tcp2ws)");
+ console.log(" -h <target host>, --thost <target host> (default " + thost + ")");
+ console.log(" -m <ws2tcp or tcp2ws>, --method <ws2tcp or tcp2ws> (default " + method + ")");
+ process.exit(0);
+ }
+
+ var lportSet = false;
+ var tportSet = false;
+ for (var i = 0; i < args.length; i++) {
+ var arg = args[i];
+ if (arg.charAt(0) === '-') {
+ i++;
+ var val = args[i];
+ if (arg === '-p' || arg === '--port') {
+ lport = val;
+ lportSet = true;
+ } else if (arg === '-t' || arg === '--tport') {
+ tport = val;
+ tportSet = true;
+ } else if (arg === '-h' || arg === '--thost') {
+ thost = val;
+ } else if (arg === '-m' || arg === '--method') {
+ method = val;
+ }
+ }
+ }
+
+ if (method === 'tcp2ws' && !lportSet) {
+ lport--;
+ }
+
+ if (!tportSet) {
+ tport = (method === 'ws2tcp') ? lport - 1 : +lport + 1;
+ }
+ }
+
+ if (method === 'tcp2ws') {
+ console.log("Proxying tcp -> ws");
+ console.log("Forwarding port " + lport + " to " + thost + ":" + tport);
+ proxy.tcp2ws(lport, thost, tport, 'AMQPWSB10');
+ } else if (method === 'ws2tcp') {
+ console.log("Proxying ws -> tcp");
+ console.log("Forwarding port " + lport + " to " + thost + ":" + tport);
+ proxy.ws2tcp(lport, thost, tport);
+ } else {
+ console.error("Method must be either ws2tcp or tcp2ws.");
+ }
+} else {
+ console.error("proxy.js should be run in Node.js");
+}
+
diff --git a/examples/messenger/javascript/qpid-config.js b/examples/messenger/javascript/qpid-config.js
new file mode 100755
index 0000000..2f2a2ee
--- /dev/null
+++ b/examples/messenger/javascript/qpid-config.js
@@ -0,0 +1,1511 @@
+#!/usr/bin/env node
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * Port of qpid-config to JavaScript for Node.js, mainly intended as a demo to
+ * illustrate using QMF2 in JavaScript using the proton.Messenger JS binding.
+ * It illustrates a few things including how to use Messenger completely
+ * asynchronously including using an async request/response pattern with
+ * correlation IDs. It also proves interoperability of AMQP Map, List etc.
+ * between C++ and JavaScript as QMF2 is pretty much all about Lists of Maps.
+ * <p>
+ * The actual QMF2 code is pretty simple as we're just doing a basic getObjects
+ * it's made all the simpler because we can use JavaScript object literals as
+ * the JavaScript binding serialises and deserialises directly between JavaScript
+ * Objects and Lists and the AMQP type system so something that can be quite
+ * involved in languages like C++ and Java becomes quite simple in JavaScript,
+ * though the asynchronous nature of JavaScript provides its own opportunities
+ * for complication best illustrated by the need for the correlator object.
+ */
+
+// Check if the environment is Node.js and if not log an error and exit.
+if (typeof process === 'object' && typeof require === 'function') {
+
+ var qmf = {}; // Create qmf namespace object.
+ qmf.Console = function() { // qmf.Console Constructor.
+ var proton = require("qpid-proton");
+ var message = new proton.Message();
+ var messenger = new proton.Messenger();
+
+ var brokerAddress = '';
+ var replyTo = '';
+
+ /**
+ * The correlator object is a mechanism used to correlate requests with
+ * their aynchronous responses. It might possible be better to make use
+ * of Promises to implement part of this behaviour but a mechanism would
+ * still be needed to correlate a request with its response callback in
+ * order to wrap things up in a Promise, so much of the behaviour of this
+ * object would still be required. In addition it seemed to make sense to
+ * make this QMF2 implementation fairly free of dependencies and using
+ * Promises would require external libraries. Instead the correlator
+ * implements "Promise-like" semantics, you might say a broken Promise :-)
+ * <p>
+ * in particular the request method behaves a *bit* like Promise.all()
+ * though it is mostly fake and takes an array of functions that call
+ * the add() method which is really the method used to associate response
+ * objects by correlationID. The then method is used to register a
+ * listener that will be called when all the requests that have been
+ * registered have received responses.
+ * TODO error/timeout handling.
+ */
+ var correlator = {
+ _resolve: null,
+ _objects: {},
+ add: function(id) {
+ this._objects[id] = {complete: false, list: null};
+ },
+ request: function() {
+ this._resolve = function() {console.log("Warning: No resolver has been set")};
+ return this;
+ },
+ then: function(resolver) {
+ this._resolve = resolver ? resolver : this._resolve;
+ },
+ resolve: function() {
+ var opcode = message.properties['qmf.opcode'];
+ var correlationID = message.getCorrelationID();
+ var resp = this._objects[correlationID];
+ if (opcode === '_query_response') {
+ if (resp.list) {
+ Array.prototype.push.apply(resp.list, message.body); // This is faster than concat.
+ } else {
+ resp.list = message.body;
+ }
+
+ var partial = message.properties['partial'];
+ if (!partial) {
+ resp.complete = true;
+ }
+
+ this._objects[correlationID] = resp;
+ this._checkComplete();
+ } else if (opcode === '_method_response' || opcode === '_exception') {
+ resp.list = message.body;
+ resp.complete = true;
+ this._objects[correlationID] = resp;
+ this._checkComplete();
+ } else {
+ console.error("Bad Message response, qmf.opcode = " + opcode);
+ }
+ },
+ _checkComplete: function() {
+ var response = {};
+ for (var id in this._objects) {
+ var object = this._objects[id];
+ if (object.complete) {
+ response[id] = object.list;
+ } else {
+ return;
+ }
+ }
+
+ this._objects = {}; // Clear state ready for next call.
+ this._resolve(response.method ? response.method : response);
+ }
+ }; // End of correlator object definition.
+
+ var pumpData = function() {
+ while (messenger.incoming()) {
+ // The second parameter forces Binary payloads to be decoded as
+ // strings this is useful because the broker QMF Agent encodes
+ // strings as AMQP binary unfortunately.
+ var t = messenger.get(message, true);
+ correlator.resolve();
+ messenger.accept(t);
+ }
+
+ if (messenger.isStopped()) {
+ message.free();
+ messenger.free();
+ }
+ };
+
+ this.getObjects = function(packageName, className) {
+ message.setAddress(brokerAddress);
+ message.setSubject('broker');
+ message.setReplyTo(replyTo);
+ message.setCorrelationID(className);
+ message.properties = {
+ "routing-key": "broker", // Added for Java Broker
+ "x-amqp-0-10.app-id": "qmf2",
+ "method": "request",
+ "qmf.opcode": "_query_request",
+ };
+ message.body = {
+ "_what": "OBJECT",
+ "_schema_id": {
+ "_package_name": packageName,
+ "_class_name": className
+ }
+ };
+
+ correlator.add(className);
+ messenger.put(message);
+ };
+
+ this.invokeMethod = function(object, method, arguments) {
+ var correlationID = 'method';
+ message.setAddress(brokerAddress);
+ message.setSubject('broker');
+ message.setReplyTo(replyTo);
+ message.setCorrelationID(correlationID);
+ message.properties = {
+ "routing-key": "broker", // Added for Java Broker
+ "x-amqp-0-10.app-id": "qmf2",
+ "method": "request",
+ "qmf.opcode": "_method_request",
+ };
+ message.body = {
+ "_object_id": object._object_id,
+ "_method_name" : method,
+ "_arguments" : arguments
+ };
+
+ correlator.add(correlationID);
+ messenger.put(message);
+ };
+
+ this.addConnection = function(addr, callback) {
+ brokerAddress = addr + '/qmf.default.direct';
+ var replyAddress = addr + '/#';
+
+ messenger.on('subscription', function(subscription) {
+ var subscriptionAddress = subscription.getAddress();
+ var splitAddress = subscriptionAddress.split('/');
+ replyTo = splitAddress[splitAddress.length - 1];
+ callback();
+ });
+
+ messenger.subscribe(replyAddress);
+ }
+
+ this.destroy = function() {
+ messenger.stop();
+ }
+
+ this.request = function() {return correlator.request();}
+
+ messenger.on('error', function(error) {console.log(error);});
+ messenger.on('work', pumpData);
+ messenger.setOutgoingWindow(1024);
+ messenger.recv(); // Receive as many messages as messenger can buffer.
+ messenger.start();
+ }; // End of qmf.Console
+
+/************************* qpid-config business logic ************************/
+
+ var brokerAgent = new qmf.Console();
+
+ var _usage =
+ 'Usage: qpid-config [OPTIONS]\n' +
+ ' qpid-config [OPTIONS] exchanges [filter-string]\n' +
+ ' qpid-config [OPTIONS] queues [filter-string]\n' +
+ ' qpid-config [OPTIONS] add exchange <type> <name> [AddExchangeOptions]\n' +
+ ' qpid-config [OPTIONS] del exchange <name>\n' +
+ ' qpid-config [OPTIONS] add queue <name> [AddQueueOptions]\n' +
+ ' qpid-config [OPTIONS] del queue <name> [DelQueueOptions]\n' +
+ ' qpid-config [OPTIONS] bind <exchange-name> <queue-name> [binding-key]\n' +
+ ' <for type xml> [-f -|filename]\n' +
+ ' <for type header> [all|any] k1=v1 [, k2=v2...]\n' +
+ ' qpid-config [OPTIONS] unbind <exchange-name> <queue-name> [binding-key]\n' +
+ ' qpid-config [OPTIONS] reload-acl\n' +
+ ' qpid-config [OPTIONS] add <type> <name> [--argument <property-name>=<property-value>]\n' +
+ ' qpid-config [OPTIONS] del <type> <name>\n' +
+ ' qpid-config [OPTIONS] list <type> [--show-property <property-name>]\n';
+
+ var usage = function() {
+ console.log(_usage);
+ process.exit(-1);
+ };
+
+ var _description =
+ 'Examples:\n' +
+ '\n' +
+ '$ qpid-config add queue q\n' +
+ '$ qpid-config add exchange direct d -a localhost:5672\n' +
+ '$ qpid-config exchanges -b 10.1.1.7:10000\n' +
+ '$ qpid-config queues -b guest/guest@broker-host:10000\n' +
+ '\n' +
+ 'Add Exchange <type> values:\n' +
+ '\n' +
+ ' direct Direct exchange for point-to-point communication\n' +
+ ' fanout Fanout exchange for broadcast communication\n' +
+ ' topic Topic exchange that routes messages using binding keys with wildcards\n' +
+ ' headers Headers exchange that matches header fields against the binding keys\n' +
+ ' xml XML Exchange - allows content filtering using an XQuery\n' +
+ '\n' +
+ '\n' +
+ 'Queue Limit Actions:\n' +
+ '\n' +
+ ' none (default) - Use broker\'s default policy\n' +
+ ' reject - Reject enqueued messages\n' +
+ ' ring - Replace oldest unacquired message with new\n' +
+ '\n' +
+ 'Replication levels:\n' +
+ '\n' +
+ ' none - no replication\n' +
+ ' configuration - replicate queue and exchange existence and bindings, but not messages.\n' +
+ ' all - replicate configuration and messages\n';
+
+ var _options =
+ 'Options:\n' +
+ ' -h, --help show this help message and exit\n' +
+ '\n' +
+ ' General Options:\n' +
+ ' -t <secs>, --timeout=<secs>\n' +
+ ' Maximum time to wait for broker connection (in\n' +
+ ' seconds)\n' +
+ ' -r, --recursive Show bindings in queue or exchange list\n' +
+ ' -b <address>, --broker=<address>\n' +
+ ' Address of qpidd broker with syntax:\n' +
+ ' [username/password@] hostname | ip-address [:<port>]\n' +
+ ' -a <address>, --broker-addr=<address>\n' +
+ /* TODO Connection options
+ ' --sasl-mechanism=<mech>\n' +
+ ' SASL mechanism for authentication (e.g. EXTERNAL,\n' +
+ ' ANONYMOUS, PLAIN, CRAM-MD5, DIGEST-MD5, GSSAPI). SASL\n' +
+ ' automatically picks the most secure available\n' +
+ ' mechanism - use this option to override.\n' +
+ ' --ssl-certificate=<cert>\n' +
+ ' Client SSL certificate (PEM Format)\n' +
+ ' --ssl-key=<key> Client SSL private key (PEM Format)\n' +
+ ' --ha-admin Allow connection to a HA backup broker.\n' +
+ */
+ '\n' +
+ ' Options for Listing Exchanges and Queues:\n' +
+ ' --ignore-default Ignore the default exchange in exchange or queue list\n' +
+ '\n' +
+ ' Options for Adding Exchanges and Queues:\n' +
+ ' --alternate-exchange=<aexname>\n' +
+ ' Name of the alternate-exchange for the new queue or\n' +
+ ' exchange. Exchanges route messages to the alternate\n' +
+ ' exchange if they are unable to route them elsewhere.\n' +
+ ' Queues route messages to the alternate exchange if\n' +
+ ' they are rejected by a subscriber or orphaned by queue\n' +
+ ' deletion.\n' +
+ ' --durable The new queue or exchange is durable.\n' +
+ ' --replicate=<level>\n' +
+ ' Enable automatic replication in a HA cluster. <level>\n' +
+ ' is \'none\', \'configuration\' or \'all\').\n' +
+ '\n' +
+ ' Options for Adding Queues:\n' +
+ ' --file-count=<n> Number of files in queue\'s persistence journal\n' +
+ ' --file-size=<n> File size in pages (64KiB/page)\n' +
+ ' --max-queue-size=<n>\n' +
+ ' Maximum in-memory queue size as bytes\n' +
+ ' --max-queue-count=<n>\n' +
+ ' Maximum in-memory queue size as a number of messages\n' +
+ ' --limit-policy=<policy>\n' +
+ ' Action to take when queue limit is reached\n' +
+ ' --lvq-key=<key> Last Value Queue key\n' +
+ ' --generate-queue-events=<n>\n' +
+ ' If set to 1, every enqueue will generate an event that\n' +
+ ' can be processed by registered listeners (e.g. for\n' +
+ ' replication). If set to 2, events will be generated\n' +
+ ' for enqueues and dequeues.\n' +
+ ' --flow-stop-size=<n>\n' +
+ ' Turn on sender flow control when the number of queued\n' +
+ ' bytes exceeds this value.\n' +
+ ' --flow-resume-size=<n>\n' +
+ ' Turn off sender flow control when the number of queued\n' +
+ ' bytes drops below this value.\n' +
+ ' --flow-stop-count=<n>\n' +
+ ' Turn on sender flow control when the number of queued\n' +
+ ' messages exceeds this value.\n' +
+ ' --flow-resume-count=<n>\n' +
+ ' Turn off sender flow control when the number of queued\n' +
+ ' messages drops below this value.\n' +
+ ' --group-header=<header-name>\n' +
+ ' Enable message groups. Specify name of header that\n' +
+ ' holds group identifier.\n' +
+ ' --shared-groups Allow message group consumption across multiple\n' +
+ ' consumers.\n' +
+ ' --argument=<NAME=VALUE>\n' +
+ ' Specify a key-value pair to add to queue arguments\n' +
+ ' --start-replica=<broker-url>\n' +
+ ' Start replication from the same-named queue at\n' +
+ ' <broker-url>\n' +
+ '\n' +
+ ' Options for Adding Exchanges:\n' +
+ ' --sequence Exchange will insert a \'qpid.msg_sequence\' field in\n' +
+ ' the message header\n' +
+ ' --ive Exchange will behave as an \'initial-value-exchange\',\n' +
+ ' keeping a reference to the last message forwarded and\n' +
+ ' enqueuing that message to newly bound queues.\n' +
+ '\n' +
+ ' Options for Deleting Queues:\n' +
+ ' --force Force delete of queue even if it\'s currently used or\n' +
+ ' it\'s not empty\n' +
+ ' --force-if-not-empty\n' +
+ ' Force delete of queue even if it\'s not empty\n' +
+ ' --force-if-used Force delete of queue even if it\'s currently used\n' +
+ '\n' +
+ ' Options for Declaring Bindings:\n' +
+ ' -f <file.xq>, --file=<file.xq>\n' +
+ ' For XML Exchange bindings - specifies the name of a\n' +
+ ' file containing an XQuery.\n' +
+ '\n' +
+ ' Formatting options for \'list\' action:\n' +
+ ' --show-property=<property-name>\n' +
+ ' Specify a property of an object to be included in\n' +
+ ' output\n';
+
+ var REPLICATE_LEVELS = {"none" : true, "configuration": true, "all": true};
+ var DEFAULT_PROPERTIES = {"exchange": {"name": true, "type": true, "durable": true},
+ "queue": {"name": true, "durable": true, "autoDelete": true}};
+
+ var getValue = function(r) {
+ var value = null;
+ if (r.length === 2) {
+ value = r[1];
+ if (!isNaN(value)) {
+ value = parseInt(value);
+ }
+ }
+
+ return value;
+ };
+
+ var config = {
+ _recursive : false,
+ _host : 'guest:guest@localhost:5673', // Note 5673 not 5672 as we use WebSocket transport.
+ _connTimeout : 10,
+ _ignoreDefault : false,
+ _altern_ex : null,
+ _durable : false,
+ _replicate : null,
+ _if_empty : true,
+ _if_unused : true,
+ _fileCount : null,
+ _fileSize : null,
+ _maxQueueSize : null,
+ _maxQueueCount : null,
+ _limitPolicy : null,
+ _msgSequence : false,
+ _lvq_key : null,
+ _ive : null,
+ _eventGeneration: null,
+ _file : null,
+ _flowStopCount : null,
+ _flowResumeCount: null,
+ _flowStopSize : null,
+ _flowResumeSize : null,
+ _msgGroupHeader : null,
+ _sharedMsgGroup : false,
+ _extra_arguments: [],
+ _start_replica : null,
+ _returnCode : 0,
+ _list_properties: null,
+
+ getOptions: function() {
+ var options = {};
+ for (var a = 0; a < this._extra_arguments.length; a++) {
+ var r = this._extra_arguments[a].split('=');
+ options[r[0]] = getValue(r);
+ }
+ return options;
+ }
+ };
+
+ var FILECOUNT = 'qpid.file_count';
+ var FILESIZE = 'qpid.file_size';
+ var MAX_QUEUE_SIZE = 'qpid.max_size';
+ var MAX_QUEUE_COUNT = 'qpid.max_count';
+ var POLICY_TYPE = 'qpid.policy_type';
+ var LVQ_KEY = 'qpid.last_value_queue_key';
+ var MSG_SEQUENCE = 'qpid.msg_sequence';
+ var IVE = 'qpid.ive';
+ var QUEUE_EVENT_GENERATION = 'qpid.queue_event_generation';
+ var FLOW_STOP_COUNT = 'qpid.flow_stop_count';
+ var FLOW_RESUME_COUNT = 'qpid.flow_resume_count';
+ var FLOW_STOP_SIZE = 'qpid.flow_stop_size';
+ var FLOW_RESUME_SIZE = 'qpid.flow_resume_size';
+ var MSG_GROUP_HDR_KEY = 'qpid.group_header_key';
+ var SHARED_MSG_GROUP = 'qpid.shared_msg_group';
+ var REPLICATE = 'qpid.replicate';
+
+ /**
+ * There are various arguments to declare that have specific program
+ * options in this utility. However there is now a generic mechanism for
+ * passing arguments as well. The SPECIAL_ARGS list contains the
+ * arguments for which there are specific program options defined
+ * i.e. the arguments for which there is special processing on add and
+ * list
+ */
+ var SPECIAL_ARGS={};
+ SPECIAL_ARGS[FILECOUNT] = true;
+ SPECIAL_ARGS[FILESIZE] = true;
+ SPECIAL_ARGS[MAX_QUEUE_SIZE] = true;
+ SPECIAL_ARGS[MAX_QUEUE_COUNT] = true;
+ SPECIAL_ARGS[POLICY_TYPE] = true;
+ SPECIAL_ARGS[LVQ_KEY] = true;
+ SPECIAL_ARGS[MSG_SEQUENCE] = true;
+ SPECIAL_ARGS[IVE] = true;
+ SPECIAL_ARGS[QUEUE_EVENT_GENERATION] = true;
+ SPECIAL_ARGS[FLOW_STOP_COUNT] = true;
+ SPECIAL_ARGS[FLOW_RESUME_COUNT] = true;
+ SPECIAL_ARGS[FLOW_STOP_SIZE] = true;
+ SPECIAL_ARGS[FLOW_RESUME_SIZE] = true;
+ SPECIAL_ARGS[MSG_GROUP_HDR_KEY] = true;
+ SPECIAL_ARGS[SHARED_MSG_GROUP] = true;
+ SPECIAL_ARGS[REPLICATE] = true;
+
+ // Returns a String representation of an ObjectID.
+ var oid = function(id) {
+ return id._agent_epoch + ':' + id._object_name
+ };
+
+ // Check if the supplied name contains the supplied filter String.
+ var filterMatch = function(name, filter) {
+ if (filter === '') {
+ return true;
+ }
+ if (name.indexOf(filter) === -1) {
+ return false;
+ }
+ return true;
+ };
+
+ // Take the supplied List of QMF2 Objects and return a Map keyed by ObjectID.
+ var idMap = function(list) {
+ var map = {};
+ for (var i = 0; i < list.length; i++) {
+ var item = list[i];
+ map[oid(item._object_id)] = item;
+ }
+ return map;
+ };
+
+ // Pretty-print the supplied Object.
+ var renderObject = function(obj, list) {
+ if (!obj) {
+ return '';
+ }
+ var string = '';
+ var addComma = false;
+ for (var prop in obj) {
+ if (addComma) {
+ string += ', ';
+ }
+ if (obj.hasOwnProperty(prop)) {
+ if (list) {
+ if (SPECIAL_ARGS[prop]) continue;
+ string += " --argument " + prop + "=" + obj[prop];
+ } else {
+ string += "'" + prop + "'" + ": '" + obj[prop] + "'";
+ addComma = true;
+ }
+ }
+ }
+
+ if (addComma) {
+ return '{' + string + '}';
+ } else {
+ if (list) {
+ return string;
+ } else {
+ return '';
+ }
+ }
+ };
+
+ /**
+ * The following methods illustrate the QMF2 class query mechanism which returns
+ * the list of QMF Objects for the specified class that are currently present
+ * on the Broker. The Schema <qpid>/cpp/src/qpid/broker/management-schema.xml
+ * describes the properties and statistics of each Management Object.
+ * <p>
+ * One slightly subtle part of QMF is that certain Objects are associated via
+ * references, for example Binding contains queueRef and exchangeRef, which lets
+ * Objects link to each other using their _object_id property.
+ * <p>
+ * The implementation of these methods attempts to follow the same general flow
+ * as the equivalent method in the "canonical" python based qpid-config version
+ * but has the added complication that JavaScript is entirely asynchronous.
+ * The approach that has been taken is to use the correlator object that lets a
+ * callback function be registered via the "then" method and actually calls the
+ * callback when all of the requests specified in the request method have
+ * returned their results (which get passed as the callback parameter).
+ */
+
+ var overview = function() {
+ brokerAgent.request(
+ // Send the QMF query requests for the specified classes.
+ brokerAgent.getObjects('org.apache.qpid.broker', 'queue'),
+ brokerAgent.getObjects('org.apache.qpid.broker', 'exchange')
+ ).then(function(objects) {
+ var exchanges = objects.exchange;
+ var queues = objects.queue;
+ console.log("Total Exchanges: " + exchanges.length);
+ var etype = {};
+ for (var i = 0; i < exchanges.length; i++) {
+ var exchange = exchanges[i]._values;
+ if (!etype[exchange.type]) {
+ etype[exchange.type] = 1;
+ } else {
+ etype[exchange.type]++;
+ }
+ }
+ for (var typ in etype) {
+ var pad = Array(16 - typ.length).join(' ');
+ console.log(pad + typ + ": " + etype[typ]);
+ }
+
+ console.log("\n Total Queues: " + queues.length);
+ var durable = 0;
+ for (var i = 0; i < queues.length; i++) {
+ var queue = queues[i]._values;
+ if (queue.durable) {
+ durable++;
+ }
+ }
+ console.log(" durable: " + durable);
+ console.log(" non-durable: " + (queues.length - durable));
+ brokerAgent.destroy();
+ });
+ };
+
+ var exchangeList = function(filter) {
+ brokerAgent.request(
+ // Send the QMF query requests for the specified classes.
+ brokerAgent.getObjects('org.apache.qpid.broker', 'exchange')
+ ).then(function(objects) {
+ var exchanges = objects.exchange;
+ var exMap = idMap(exchanges);
+ var caption1 = "Type ";
+ var caption2 = "Exchange Name";
+ var maxNameLen = caption2.length;
+ var found = false;
+ for (var i = 0; i < exchanges.length; i++) {
+ var exchange = exchanges[i]._values;
+ if (filterMatch(exchange.name, filter)) {
+ if (exchange.name.length > maxNameLen) {
+ maxNameLen = exchange.name.length;
+ }
+ found = true;
+ }
+ }
+ if (!found) {
+ config._returnCode = 1;
+ return;
+ }
+
+ var pad = Array(maxNameLen + 1 - caption2.length).join(' ');
+ console.log(caption1 + caption2 + pad + " Attributes");
+ console.log(Array(maxNameLen + caption1.length + 13).join('='));
+
+ for (var i = 0; i < exchanges.length; i++) {
+ var exchange = exchanges[i]._values;
+ if (config._ignoreDefault && !exchange.name) continue;
+ if (filterMatch(exchange.name, filter)) {
+ var pad1 = Array(11 - exchange.type.length).join(' ');
+ var pad2 = Array(maxNameLen + 2 - exchange.name.length).join(' ');
+ var string = exchange.type + pad1 + exchange.name + pad2;
+ var args = exchange.arguments ? exchange.arguments : {};
+ if (exchange.durable) {
+ string += ' --durable';
+ }
+ if (args[REPLICATE]) {
+ string += ' --replicate=' + args[REPLICATE];
+ }
+ if (args[MSG_SEQUENCE]) {
+ string += ' --sequence';
+ }
+ if (args[IVE]) {
+ string += ' --ive';
+ }
+ if (exchange.altExchange) {
+ string += ' --alternate-exchange=' + exMap[oid(exchange.altExchange)]._values.name;
+ }
+ console.log(string);
+ }
+ }
+ brokerAgent.destroy();
+ });
+ };
+
+ var exchangeListRecurse = function(filter) {
+ brokerAgent.request(
+ // Send the QMF query requests for the specified classes.
+ brokerAgent.getObjects('org.apache.qpid.broker', 'queue'),
+ brokerAgent.getObjects('org.apache.qpid.broker', 'exchange'),
+ brokerAgent.getObjects('org.apache.qpid.broker', 'binding')
+ ).then(function(objects) {
+ var exchanges = objects.exchange;
+ var bindings = objects.binding;
+ var queues = idMap(objects.queue);
+
+ for (var i = 0; i < exchanges.length; i++) {
+ var exchange = exchanges[i];
+ var exchangeId = oid(exchange._object_id);
+ exchange = exchange._values;
+
+ if (config._ignoreDefault && !exchange.name) continue;
+ if (filterMatch(exchange.name, filter)) {
+ console.log("Exchange '" + exchange.name + "' (" + exchange.type + ")");
+ for (var j = 0; j < bindings.length; j++) {
+ var bind = bindings[j]._values;
+ var exchangeRef = oid(bind.exchangeRef);
+
+ if (exchangeRef === exchangeId) {
+ var queue = queues[oid(bind.queueRef)];
+ var queueName = queue ? queue._values.name : "<unknown>";
+ console.log(" bind [" + bind.bindingKey + "] => " + queueName +
+ " " + renderObject(bind.arguments));
+ }
+ }
+ }
+ }
+ brokerAgent.destroy();
+ });
+ };
+
+ var queueList = function(filter) {
+ brokerAgent.request(
+ // Send the QMF query requests for the specified classes.
+ brokerAgent.getObjects('org.apache.qpid.broker', 'queue'),
+ brokerAgent.getObjects('org.apache.qpid.broker', 'exchange')
+ ).then(function(objects) {
+ var queues = objects.queue;
+ var exMap = idMap(objects.exchange);
+ var caption = "Queue Name";
+ var maxNameLen = caption.length;
+ var found = false;
+ for (var i = 0; i < queues.length; i++) {
+ var queue = queues[i]._values;
+ if (filterMatch(queue.name, filter)) {
+ if (queue.name.length > maxNameLen) {
+ maxNameLen = queue.name.length;
+ }
+ found = true;
+ }
+ }
+ if (!found) {
+ config._returnCode = 1;
+ return;
+ }
+
+ var pad = Array(maxNameLen + 1 - caption.length).join(' ');
+ console.log(caption + pad + " Attributes");
+ console.log(Array(maxNameLen + caption.length + 3).join('='));
+
+ for (var i = 0; i < queues.length; i++) {
+ var queue = queues[i]._values;
+ if (filterMatch(queue.name, filter)) {
+ var pad2 = Array(maxNameLen + 2 - queue.name.length).join(' ');
+ var string = queue.name + pad2;
+ var args = queue.arguments ? queue.arguments : {};
+ if (queue.durable) {
+ string += ' --durable';
+ }
+ if (args[REPLICATE]) {
+ string += ' --replicate=' + args[REPLICATE];
+ }
+ if (queue.autoDelete) {
+ string += ' auto-del';
+ }
+ if (queue.exclusive) {
+ string += ' excl';
+ }
+ if (args[FILESIZE]) {
+ string += ' --file-size=' + args[FILESIZE];
+ }
+ if (args[FILECOUNT]) {
+ string += ' --file-count=' + args[FILECOUNT];
+ }
+ if (args[MAX_QUEUE_SIZE]) {
+ string += ' --max-queue-size=' + args[MAX_QUEUE_SIZE];
+ }
+ if (args[MAX_QUEUE_COUNT]) {
+ string += ' --max-queue-count=' + args[MAX_QUEUE_COUNT];
+ }
+ if (args[POLICY_TYPE]) {
+ string += ' --limit-policy=' + args[POLICY_TYPE].replace("_", "-");
+ }
+ if (args[LVQ_KEY]) {
+ string += ' --lvq-key=' + args[LVQ_KEY];
+ }
+ if (args[QUEUE_EVENT_GENERATION]) {
+ string += ' --generate-queue-events=' + args[QUEUE_EVENT_GENERATION];
+ }
+ if (queue.altExchange) {
+ string += ' --alternate-exchange=' + exMap[oid(queue.altExchange)]._values.name;
+ }
+ if (args[FLOW_STOP_SIZE]) {
+ string += ' --flow-stop-size=' + args[FLOW_STOP_SIZE];
+ }
+ if (args[FLOW_RESUME_SIZE]) {
+ string += ' --flow-resume-size=' + args[FLOW_RESUME_SIZE];
+ }
+ if (args[FLOW_STOP_COUNT]) {
+ string += ' --flow-stop-count=' + args[FLOW_STOP_COUNT];
+ }
+ if (args[FLOW_RESUME_COUNT]) {
+ string += ' --flow-resume-count=' + args[FLOW_RESUME_COUNT];
+ }
+ if (args[MSG_GROUP_HDR_KEY]) {
+ string += ' --group-header=' + args[MSG_GROUP_HDR_KEY];
+ }
+ if (args[SHARED_MSG_GROUP] === 1) {
+ string += ' --shared-groups';
+ }
+ string += ' ' + renderObject(args, true);
+ console.log(string);
+ }
+ }
+ brokerAgent.destroy();
+ });
+ };
+
+ var queueListRecurse = function(filter) {
+ brokerAgent.request(
+ // Send the QMF query requests for the specified classes.
+ brokerAgent.getObjects('org.apache.qpid.broker', 'queue'),
+ brokerAgent.getObjects('org.apache.qpid.broker', 'exchange'),
+ brokerAgent.getObjects('org.apache.qpid.broker', 'binding')
+ ).then(function(objects) {
+ var queues = objects.queue;
+ var bindings = objects.binding;
+ var exchanges = idMap(objects.exchange);
+
+ for (var i = 0; i < queues.length; i++) {
+ var queue = queues[i];
+ var queueId = oid(queue._object_id);
+ queue = queue._values;
+
+ if (filterMatch(queue.name, filter)) {
+ console.log("Queue '" + queue.name + "'");
+ for (var j = 0; j < bindings.length; j++) {
+ var bind = bindings[j]._values;
+ var queueRef = oid(bind.queueRef);
+
+ if (queueRef === queueId) {
+ var exchange = exchanges[oid(bind.exchangeRef)];
+ var exchangeName = "<unknown>";
+ if (exchange) {
+ exchangeName = exchange._values.name;
+ if (exchangeName === '') {
+ if (config._ignoreDefault) continue;
+ exchangeName = "''";
+ }
+ }
+
+ console.log(" bind [" + bind.bindingKey + "] => " + exchangeName +
+ " " + renderObject(bind.arguments));
+ }
+ }
+ }
+ }
+ brokerAgent.destroy();
+ });
+ };
+
+ /**
+ * The following methods implement adding and deleting various Broker Management
+ * Objects via QMF. Although <qpid>/cpp/src/qpid/broker/management-schema.xml
+ * describes the basic method schema, for example:
+ * <method name="create" desc="Create an object of the specified type">
+ * <arg name="type" dir="I" type="sstr" desc="The type of object to create"/>
+ * <arg name="name" dir="I" type="sstr" desc="The name of the object to create"/>
+ * <arg name="properties" dir="I" type="map" desc="Type specific object properties"/>
+ * <arg name="strict" dir="I" type="bool" desc="If specified, treat unrecognised object properties as an error"/ >
+ * </method>
+ *
+ * <method name="delete" desc="Delete an object of the specified type">
+ * <arg name="type" dir="I" type="sstr" desc="The type of object to delete"/>
+ * <arg name="name" dir="I" type="sstr" desc="The name of the object to delete"/>
+ * <arg name="options" dir="I" type="map" desc="Type specific object options for deletion"/>
+ * </method>
+ *
+ * What the schema doesn't do however is to explain what the properties/options
+ * Map values actually mean, unfortunately these aren't documented anywhere so
+ * the only option is to look in the code, the best place to look is in:
+ * <qpid>/cpp/src/qpid/broker/Broker.cpp, the method Broker::ManagementMethod is
+ * the best place to start, then Broker::createObject and Broker::deleteObject
+ * even then it's pretty hard to figure out all that is possible.
+ */
+
+ var handleMethodResponse = function(response, dontStop) {
+ if (response._arguments) {
+ //console.log(response._arguments);
+ } if (response._values) {
+ console.error("Exception from Agent: " + renderObject(response._values));
+ }
+ // Mostly we want to stop the Messenger Event loop and exit when a QMF method
+ // returns, but sometimes we don't, the dontStop flag prevents this behaviour.
+ if (!dontStop) {
+ brokerAgent.destroy();
+ }
+ }
+
+ var addExchange = function(args) {
+ if (args.length < 2) {
+ usage();
+ }
+
+ var etype = args[0];
+ var ename = args[1];
+ var declArgs = {};
+
+ declArgs['exchange-type'] = etype;
+
+ for (var a = 0; a < config._extra_arguments.length; a++) {
+ var r = config._extra_arguments[a].split('=');
+ declArgs[r[0]] = getValue(r);
+ }
+
+ if (config._msgSequence) {
+ declArgs[MSG_SEQUENCE] = 1;
+ }
+
+ if (config._ive) {
+ declArgs[IVE] = 1;
+ }
+
+ if (config._altern_ex) {
+ declArgs['alternate-exchange'] = config._altern_ex;
+ }
+
+ if (config._durable) {
+ declArgs['durable'] = 1;
+ }
+
+ if (config._replicate) {
+ declArgs[REPLICATE] = config._replicate;
+ }
+
+ brokerAgent.request(
+ // We invoke the CRUD methods on the broker object.
+ brokerAgent.getObjects('org.apache.qpid.broker', 'broker')
+ ).then(function(objects) {
+ var broker = objects.broker[0];
+ brokerAgent.request(
+ brokerAgent.invokeMethod(broker, 'create', {
+ "type": "exchange",
+ "name": ename,
+ "properties": declArgs,
+ "strict": true})
+ ).then(handleMethodResponse);
+ });
+ };
+
+ var delExchange = function(args) {
+ if (args.length < 1) {
+ usage();
+ }
+
+ var ename = args[0];
+
+ brokerAgent.request(
+ // We invoke the CRUD methods on the broker object.
+ brokerAgent.getObjects('org.apache.qpid.broker', 'broker')
+ ).then(function(objects) {
+ var broker = objects.broker[0];
+ brokerAgent.request(
+ brokerAgent.invokeMethod(broker, 'delete', {
+ "type": "exchange",
+ "name": ename})
+ ).then(handleMethodResponse);
+ });
+ };
+
+ var addQueue = function(args) {
+ if (args.length < 1) {
+ usage();
+ }
+
+ var qname = args[0];
+ var declArgs = {};
+
+ for (var a = 0; a < config._extra_arguments.length; a++) {
+ var r = config._extra_arguments[a].split('=');
+ declArgs[r[0]] = getValue(r);
+ }
+
+ if (config._durable) {
+ // allow the default fileCount and fileSize specified
+ // in qpid config file to take prededence
+ if (config._fileCount) {
+ declArgs[FILECOUNT] = config._fileCount;
+ }
+ if (config._fileSize) {
+ declArgs[FILESIZE] = config._fileSize;
+ }
+ }
+
+ if (config._maxQueueSize != null) {
+ declArgs[MAX_QUEUE_SIZE] = config._maxQueueSize;
+ }
+
+ if (config._maxQueueCount != null) {
+ declArgs[MAX_QUEUE_COUNT] = config._maxQueueCount;
+ }
+
+ if (config._limitPolicy) {
+ if (config._limitPolicy === 'none') {
+ } else if (config._limitPolicy === 'reject') {
+ declArgs[POLICY_TYPE] = 'reject';
+ } else if (config._limitPolicy === 'ring') {
+ declArgs[POLICY_TYPE] = 'ring';
+ }
+ }
+
+ if (config._lvq_key) {
+ declArgs[LVQ_KEY] = config._lvq_key;
+ }
+
+ if (config._eventGeneration) {
+ declArgs[QUEUE_EVENT_GENERATION] = config._eventGeneration;
+ }
+
+ if (config._flowStopSize != null) {
+ declArgs[FLOW_STOP_SIZE] = config._flowStopSize;
+ }
+
+ if (config._flowResumeSize != null) {
+ declArgs[FLOW_RESUME_SIZE] = config._flowResumeSize;
+ }
+
+ if (config._flowStopCount != null) {
+ declArgs[FLOW_STOP_COUNT] = config._flowStopCount;
+ }
+
+ if (config._flowResumeCount != null) {
+ declArgs[FLOW_RESUME_COUNT] = config._flowResumeCount;
+ }
+
+ if (config._msgGroupHeader) {
+ declArgs[MSG_GROUP_HDR_KEY] = config._msgGroupHeader;
+ }
+
+ if (config._sharedMsgGroup) {
+ declArgs[SHARED_MSG_GROUP] = 1;
+ }
+
+ if (config._altern_ex) {
+ declArgs['alternate-exchange'] = config._altern_ex;
+ }
+
+ if (config._durable) {
+ declArgs['durable'] = 1;
+ }
+
+ if (config._replicate) {
+ declArgs[REPLICATE] = config._replicate;
+ }
+
+ // This block is a little complex and untidy, the real issue is that the
+ // correlator object isn't as good as a real Promise and doesn't support
+ // chaining of "then" calls, so where we have complex dependencies we still
+ // get somewhat into "callback hell". TODO improve the correlator.
+ brokerAgent.request(
+ // We invoke the CRUD methods on the broker object.
+ brokerAgent.getObjects('org.apache.qpid.broker', 'broker')
+ ).then(function(objects) {
+ var broker = objects.broker[0];
+ brokerAgent.request(
+ brokerAgent.invokeMethod(broker, 'create', {
+ "type": "queue",
+ "name": qname,
+ "properties": declArgs,
+ "strict": true})
+ ).then(function(response) {
+ if (config._start_replica) {
+ handleMethodResponse(response, true); // The second parameter prevents exiting.
+ // TODO test this stuff!
+ brokerAgent.request(
+ brokerAgent.getObjects('org.apache.qpid.ha', 'habroker') // Not sure if this is correct
+ ).then(function(objects) {
+ if (objects.habroker.length > 0) {
+ var habroker = objects.habroker[0];
+ brokerAgent.request(
+ brokerAgent.invokeMethod(habroker, 'replicate', {
+ "broker": config._start_replica,
+ "queue": qname})
+ ).then(handleMethodResponse);
+ } else {
+ brokerAgent.destroy();
+ }
+ });
+ } else {
+ handleMethodResponse(response);
+ }
+ });
+ });
+ };
+
+ var delQueue = function(args) {
+ if (args.length < 1) {
+ usage();
+ }
+
+ var qname = args[0];
+
+ brokerAgent.request(
+ // We invoke the CRUD methods on the broker object.
+ brokerAgent.getObjects('org.apache.qpid.broker', 'broker')
+ ).then(function(objects) {
+ var broker = objects.broker[0];
+ brokerAgent.request(
+ brokerAgent.invokeMethod(broker, 'delete', {
+ "type": "queue",
+ "name": qname,
+ "options": {"if_empty": config._if_empty,
+ "if_unused": config._if_unused}})
+ ).then(handleMethodResponse);
+ });
+ };
+
+ var snarf_header_args = function(args) {
+ if (args.length < 2) {
+ console.log("Invalid args to bind headers: need 'any'/'all' plus conditions");
+ return false;
+ }
+
+ var op = args[0];
+ if (op === 'all' || op === 'any') {
+ kv = {};
+ var bindings = Array.prototype.slice.apply(args, [1]);
+ for (var i = 0; i < bindings.length; i++) {
+ var binding = bindings[i];
+ binding = binding.split(",")[0];
+ binding = binding.split("=");
+ kv[binding[0]] = binding[1];
+ }
+ kv['x-match'] = op;
+ return kv;
+ } else {
+ console.log("Invalid condition arg to bind headers, need 'any' or 'all', not '" + op + "'");
+ return false;
+ }
+ };
+
+ var bind = function(args) {
+ if (args.length < 2) {
+ usage();
+ }
+
+ var ename = args[0];
+ var qname = args[1];
+ var key = '';
+
+ if (args.length > 2) {
+ key = args[2];
+ }
+
+ brokerAgent.request(
+ // We invoke the CRUD methods on the broker object.
+ brokerAgent.getObjects('org.apache.qpid.broker', 'broker'),
+ brokerAgent.getObjects('org.apache.qpid.broker', 'exchange') // Get exchanges to look up exchange type.
+ ).then(function(objects) {
+ var exchanges = objects.exchange;
+
+ var etype = '';
+ for (var i = 0; i < exchanges.length; i++) {
+ var exchange = exchanges[i]._values;
+ if (exchange.name === ename) {
+ etype = exchange.type;
+ break;
+ }
+ }
+
+ // type of the xchg determines the processing of the rest of
+ // argv. if it's an xml xchg, we want to find a file
+ // containing an x-query, and pass that. if it's a headers
+ // exchange, we need to pass either "any" or all, followed by a
+ // map containing key/value pairs. if neither of those, extra
+ // args are ignored.
+ var declArgs = {};
+ if (etype === 'xml') {
+
+
+ } else if (etype === 'headers') {
+ declArgs = snarf_header_args(Array.prototype.slice.apply(args, [3]));
+ }
+ //console.log(declArgs);
+
+ if (typeof declArgs !== 'object') {
+ process.exit(1);
+ }
+
+ var broker = objects.broker[0];
+ brokerAgent.request(
+ brokerAgent.invokeMethod(broker, 'create', {
+ "type": "binding",
+ "name": ename + '/' + qname + '/' + key,
+ "properties": declArgs,
+ "strict": true})
+ ).then(handleMethodResponse);
+ });
+
+ /*
+
+ ok = True
+ _args = {}
+ if not res:
+ pass
+ elif res.type == "xml":
+ # this checks/imports the -f arg
+ [ok, xquery] = snarf_xquery_args()
+ _args = { "xquery" : xquery }
+ else:
+ if res.type == "headers":
+ [ok, op, kv] = snarf_header_args(args[3:])
+ _args = kv
+ _args["x-match"] = op
+
+ if not ok:
+ sys.exit(1)
+
+ self.broker.bind(ename, qname, key, _args)
+ */
+
+ };
+
+ var unbind = function(args) {
+ if (args.length < 2) {
+ usage();
+ }
+
+ var ename = args[0];
+ var qname = args[1];
+ var key = '';
+
+ if (args.length > 2) {
+ key = args[2];
+ }
+
+ brokerAgent.request(
+ // We invoke the CRUD methods on the broker object.
+ brokerAgent.getObjects('org.apache.qpid.broker', 'broker')
+ ).then(function(objects) {
+ var broker = objects.broker[0];
+ brokerAgent.request(
+ brokerAgent.invokeMethod(broker, 'delete', {
+ "type": "binding",
+ "name": ename + '/' + qname + '/' + key})
+ ).then(handleMethodResponse);
+ });
+ };
+
+ /**
+ * The following methods are "generic" create and delete methods to for arbitrary
+ * Management Objects e.g. Incoming, Outgoing, Domain, Topic, QueuePolicy,
+ * TopicPolicy etc. use --argument k1=v1 --argument k2=v2 --argument k3=v3 to
+ * pass arbitrary arguments as key/value pairs to the Object being created/deleted,
+ * for example to add a topic object that uses the fanout exchange:
+ * ./qpid-config.js add topic fanout --argument exchange=amq.fanout \
+ * --argument qpid.max_size=1000000 --argument qpid.policy_type=ring
+ */
+
+ var createObject = function(type, name, args) {
+ brokerAgent.request(
+ // We invoke the CRUD methods on the broker object.
+ brokerAgent.getObjects('org.apache.qpid.broker', 'broker')
+ ).then(function(objects) {
+ var broker = objects.broker[0];
+ brokerAgent.request(
+ // Create an object of the specified type.
+ brokerAgent.invokeMethod(broker, 'create', {
+ "type": type,
+ "name": name,
+ "properties": args,
+ "strict": true})
+ ).then(handleMethodResponse);
+ });
+ };
+
+ var deleteObject = function(type, name, args) {
+ brokerAgent.request(
+ // We invoke the CRUD methods on the broker object.
+ brokerAgent.getObjects('org.apache.qpid.broker', 'broker')
+ ).then(function(objects) {
+ var broker = objects.broker[0];
+ brokerAgent.request(
+ // Create an object of the specified type and name.
+ brokerAgent.invokeMethod(broker, 'delete', {
+ "type": type,
+ "name": name,
+ "options": args})
+ ).then(handleMethodResponse);
+ });
+ };
+
+ /**
+ * This is a "generic" mechanism for listing arbitrary Management Objects.
+ */
+ var listObjects = function(type) {
+ brokerAgent.request(
+ brokerAgent.getObjects('org.apache.qpid.broker', type)
+ ).then(function(objects) {
+ // The correlator passes an object containing responses for all of the
+ // supplied requests so we index it by the supplied type to get our response.
+ objects = objects[type];
+
+ // Collect available attributes, stringify the values and compute the max
+ // length of the value of each attribute so that we can later create a table.
+ var attributes = {};
+ var lengths = {};
+ for (var i = 0; i < objects.length; i++) {
+ var object = objects[i];
+ object = object._values;
+ for (var prop in object) {
+ if (typeof object[prop] === 'object') { // Stringify Object properties.
+ // Check if property is an ObjectID (reference property),
+ // if so replace with the "name" part of the OID.
+ if (object[prop]['_object_name']) {
+ var parts = object[prop]['_object_name'].split(':');
+ object[prop] = parts[parts.length - 1];
+ } else {
+ // Stringify general Object properties.
+ object[prop] = renderObject(object[prop]);
+ }
+ } else {
+ object[prop] = object[prop].toString(); // Stringify other property types.
+ }
+
+ if (!lengths[prop] || object[prop].length > lengths[prop]) { // Compute lengths.
+ lengths[prop] = object[prop].length > prop.length ? object[prop].length : prop.length;
+ }
+
+ if (!config._list_properties || config._list_properties[prop]) { // Do we want this property?
+ attributes[prop] = true;
+ }
+ }
+ }
+
+ if (!config._list_properties && DEFAULT_PROPERTIES[type]) {
+ attributes = DEFAULT_PROPERTIES[type];
+ }
+
+ // Using the information we've previously prepared now render a table
+ // showing the required property values.
+ var desired = [];
+ var header = ''; // Table header showing the property names.
+ if (attributes['name']) {
+ desired.push('name');
+ delete attributes['name'];
+ header += 'name' + Array(lengths['name'] + 2 - 4).join(' ');
+ }
+
+ for (var prop in attributes) {
+ desired.push(prop);
+ header += prop + Array(lengths[prop] + 2 - prop.length).join(' ');
+ }
+
+ console.log("Objects of type '" + type + "'");
+ console.log(header);
+ console.log(Array(header.length).join('='));
+ for (var i = 0; i < objects.length; i++) {
+ var object = objects[i];
+ object = object._values;
+ var string = '';
+ for (var j = 0; j < desired.length; j++) {
+ var key = desired[j];
+ string += object[key] + Array(lengths[key] + 2 - object[key].length).join(' ');
+ }
+
+ console.log(string);
+ }
+
+ brokerAgent.destroy();
+ });
+ };
+
+ var reloadAcl = function() {
+ brokerAgent.request(
+ brokerAgent.getObjects('org.apache.qpid.acl', 'acl')
+ ).then(function(objects) {
+ if (objects.acl.length > 0) {
+ var acl = objects.acl[0];
+ brokerAgent.request(
+ // Create an object of the specified type.
+ brokerAgent.invokeMethod(acl, 'reloadACLFile', {})
+ ).then(handleMethodResponse);
+ } else {
+ console.log("Failed: No ACL Loaded in Broker");
+ brokerAgent.destroy();
+ }
+ });
+ };
+
+
+ /********************* process command line options **********************/
+
+ var params = [];
+ var extra_arguments = [];
+ var args = process.argv.slice(2);
+ if (args.length > 0) {
+ if (args[0] === '-h' || args[0] === '--help') {
+ console.log(_usage);
+ console.log(_description);
+ console.log(_options);
+ process.exit(0);
+ }
+
+ for (var i = 0; i < args.length; i++) {
+ var arg = args[i];
+ if (arg === '-r' || arg === '--recursive') {
+ config._recursive = true;
+ } else if (arg === '--ignore-default') {
+ config._ignoreDefault = true;
+ } else if (arg === '--durable') {
+ config._durable = true;
+ } else if (arg === '--shared-groups') {
+ config._sharedMsgGroup = true;
+ } else if (arg === '--sequence') {
+ config._sequence = true;
+ } else if (arg === '--ive') {
+ config._ive = true;
+ } else if (arg === '--force') {
+ config._if_empty = false;
+ config._if_unused = false;
+ } else if (arg === '--force-if-not-empty') {
+ config._if_empty = false;
+ } else if (arg === '--force-if-used') {
+ config._if_unused = false;
+ } else if (arg === '--sequence') {
+ config._msgSequence = true;
+ } else if (arg.charAt(0) === '-') {
+ i++;
+ var val = args[i];
+ if (arg === '-t' || arg === '--timeout') {
+ config._connTimeout = parseInt(val);
+ if (config._connTimeout === 0) {
+ config._connTimeout = null;
+ }
+ } else if (arg === '-b' || arg === '--broker' || arg === '-a' || arg === '--broker-addr') {
+ if (val != null) {
+ config._host = val;
+ }
+ } else if (arg === '--alternate-exchange') {
+ config._altern_ex = val;
+ } else if (arg === '--replicate') {
+ if (!REPLICATE_LEVELS[val]) {
+ console.error("Invalid replication level " + val + ", should be one of 'none', 'configuration' or 'all'");
+ }
+ config._replicate = val;
+ } else if (arg === '--file-count') {
+ config._fileCount = parseInt(val);
+ } else if (arg === '--file-size') {
+ config._fileSize = parseInt(val);
+ } else if (arg === '--max-queue-size') {
+ config._maxQueueSize = parseInt(val);
+ } else if (arg === '--max-queue-count') {
+ config._maxQueueCount = parseInt(val);
+ } else if (arg === '--limit-policy') {
+ config._limitPolicy = val;
+ } else if (arg === '--lvq-key') {
+ config._lvq_key = val;
+ } else if (arg === '--generate-queue-events') {
+ config._eventGeneration = parseInt(val);
+ } else if (arg === '--flow-stop-size') {
+ config._flowStopSize = parseInt(val);
+ } else if (arg === '--flow-resume-size') {
+ config._flowResumeSize = parseInt(val);
+ } else if (arg === '--flow-stop-count') {
+ config._flowStopCount = parseInt(val);
+ } else if (arg === '--flow-resume-count') {
+ config._flowResumeCount = parseInt(val);
+ } else if (arg === '--group-header') {
+ config._msgGroupHeader = val;
+ } else if (arg === '--argument') {
+ extra_arguments.push(val);
+ } else if (arg === '--start-replica') {
+ config._start_replica = val;
+ } else if (arg === '--f' || arg === '--file') { // TODO Won't work in node.js
+ config._file = val;
+ } else if (arg === '--show-property') {
+ if (config._list_properties === null) {
+ config._list_properties = {};
+ }
+ config._list_properties[val] = true;
+ }
+ } else {
+ params.push(arg);
+ }
+ }
+ }
+
+ config._extra_arguments = extra_arguments;
+
+ // The command only *actually* gets called when the QMF connection has actually
+ // been established so we wrap up the function we want to get called in a lambda.
+ var command = function() {overview();};
+ if (params.length > 0) {
+ var cmd = params[0];
+ var modifier = '';
+ if (params.length > 1) {
+ modifier = params[1];
+ }
+
+ if (cmd === 'exchanges') {
+ if (config._recursive) {
+ command = function() {exchangeListRecurse(modifier);};
+ } else {
+ command = function() {exchangeList(modifier);};
+ }
+ } else if (cmd === 'queues') {
+ if (config._recursive) {
+ command = function() {queueListRecurse(modifier);};
+ } else {
+ command = function() {queueList(modifier);};
+ }
+ } else if (cmd === 'add') {
+ if (modifier === 'exchange') {
+ command = function() {addExchange(Array.prototype.slice.apply(params, [2]));};
+ } else if (modifier === 'queue') {
+ command = function() {addQueue(Array.prototype.slice.apply(params, [2]));};
+ } else if (params.length > 2) {
+ command = function() {createObject(modifier, params[2], config.getOptions());};
+ } else {
+ usage();
+ }
+ } else if (cmd === 'del') {
+ if (modifier === 'exchange') {
+ command = function() {delExchange(Array.prototype.slice.apply(params, [2]));};
+ } else if (modifier === 'queue') {
+ command = function() {delQueue(Array.prototype.slice.apply(params, [2]));};
+ } else if (params.length > 2) {
+ command = function() {deleteObject(modifier, params[2], {});};
+ } else {
+ usage();
+ }
+ } else if (cmd === 'bind') {
+ command = function() {bind(Array.prototype.slice.apply(params, [1]));};
+ } else if (cmd === 'unbind') {
+ command = function() {unbind(Array.prototype.slice.apply(params, [1]));};
+ } else if (cmd === 'reload-acl') {
+ command = function() {reloadAcl();};
+ } else if (cmd === 'list' && params.length > 1) {
+ command = function() {listObjects(modifier);};
+ } else {
+ usage();
+ }
+ }
+
+ //console.log(config._host);
+ brokerAgent.addConnection(config._host, command);
+} else {
+ console.error("qpid-config.js should be run in Node.js");
+}
+
diff --git a/examples/messenger/javascript/recv.js b/examples/messenger/javascript/recv.js
new file mode 100644
index 0000000..ab5fcf1
--- /dev/null
+++ b/examples/messenger/javascript/recv.js
@@ -0,0 +1,69 @@
+#!/usr/bin/env node
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Check if the environment is Node.js and if not log an error and exit.
+if (typeof process === 'object' && typeof require === 'function') {
+ var proton = require("qpid-proton");
+
+ var address = "amqp://~0.0.0.0";
+ var message = new proton.Message();
+ var messenger = new proton.Messenger();
+
+ var pumpData = function() {
+ while (messenger.incoming()) {
+ var t = messenger.get(message);
+
+ console.log("Address: " + message.getAddress());
+ console.log("Subject: " + message.getSubject());
+
+ // body is the body as a native JavaScript Object, useful for most real cases.
+ //console.log("Content: " + message.body);
+
+ // data is the body as a proton.Data Object, used in this case because
+ // format() returns exactly the same representation as recv.c
+ console.log("Content: " + message.data.format());
+
+ messenger.accept(t);
+ }
+ };
+
+ var args = process.argv.slice(2);
+ if (args.length > 0) {
+ if (args[0] === '-h' || args[0] === '--help') {
+ console.log("Usage: node recv.js <addr> (default " + address + ")");
+ process.exit(0);
+ }
+
+ address = args[0];
+ }
+
+ messenger.setIncomingWindow(1024);
+
+ messenger.on('error', function(error) {console.log(error);});
+ messenger.on('work', pumpData);
+ messenger.recv(); // Receive as many messages as messenger can buffer.
+ messenger.start();
+
+ messenger.subscribe(address);
+} else {
+ console.error("recv.js should be run in Node.js");
+}
+
diff --git a/examples/messenger/javascript/send.html b/examples/messenger/javascript/send.html
new file mode 100644
index 0000000..c61e3f2
--- /dev/null
+++ b/examples/messenger/javascript/send.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html> <!-- HTML5 doctype -->
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+<html>
+
+<head>
+ <title>Simple Proton Messenger Send Example</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8" />
+
+<!--
+ Import the Messenger Binding proton.js. Note that this simple example pulls
+ it from the node_modules/qpid-proton/lib, which is created by the build process
+ so that the node.js based examples "just work", in a real Web App you would
+ clearly need to copy the proton.js to your own server.
+
+ In actual fact the CMake build actually builds proton.js into the directory:
+ <build>/proton-c/bindings/javascript
+ where <build> is the build directory created to run cmake from, it is then
+ copied to the node_modules/qpid-proton/lib directory.
+
+ In this example we also set the global variable PROTON_TOTAL_MEMORY in order to
+ increase the virtual heap available to the emscripten compiled C runtime. It
+ is not really necessary to do this for this application as the default value
+ of 16777216 is fine, it is simply done here to illustrate how to do it.
+-->
+<script type="text/javascript">PROTON_TOTAL_MEMORY = 50000000;</script>
+<script type="text/javascript" src="../../../node_modules/qpid-proton/lib/proton.js"></script>
+
+<script type="text/javascript">
+var message = new proton.Message();
+var messenger = new proton.Messenger();
+
+var sendMessage = function() {
+ var address = document.getElementById("address").value;
+ var subject = document.getElementById("subject").value;
+ var body = document.getElementById("body").value;
+
+console.log("sendMessage");
+console.log("address = " + address);
+console.log("subject = " + subject);
+console.log("body = " + body);
+
+ message.setAddress(address);
+ message.setSubject(subject);
+ message.body = body;
+
+ messenger.put(message);
+};
+
+var errorHandler = function(error) {
+ console.log("Received error " + error);
+};
+
+messenger.on('error', errorHandler);
+messenger.start();
+
+</script>
+
+<style>
+body
+{
+ font: 13px/1.5 Helvetica, Arial, 'Liberation Sans', FreeSans, sans-serif;
+ overflow-x: hidden; /* Hide horizontal scrollbar */
+ background: #dddddd;
+}
+
+label
+{
+ display: block;
+ font-size: 17px;
+}
+
+input, textarea
+{
+ font-size: 13px;
+ margin-bottom: 10px;
+}
+</style>
+
+</head>
+
+<body>
+<div>
+ <label for="address">Address:</label>
+ <input type="text" id="address" size="40"
+ placeholder="amqp://user:password@host:port"
+ name="address" value="amqp://guest:guest@0.0.0.0" />
+</div>
+<div>
+ <label for="subject">Subject:</label>
+ <input type="text" id="subject" size="40"
+ name="subject" value="Browser Message" />
+</div>
+<div>
+ <label for="body">Message:</label>
+ <textarea id="body" name="body" rows="4" cols="40">Hello From Browser!</textarea>
+</div>
+<div>
+ <input type="button" value="send" onclick="sendMessage()"/>
+</div>
+</body>
+
+</html>
diff --git a/examples/messenger/javascript/send.js b/examples/messenger/javascript/send.js
new file mode 100644
index 0000000..39d40bd
--- /dev/null
+++ b/examples/messenger/javascript/send.js
@@ -0,0 +1,105 @@
+#!/usr/bin/env node
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Check if the environment is Node.js and if not log an error and exit.
+if (typeof process === 'object' && typeof require === 'function') {
+ // In this example we also set the global variable PROTON_TOTAL_MEMORY in order
+ // to increase the virtual heap available to the emscripten compiled C runtime.
+ // It is not really necessary to do this for this application as the default
+ // value of 16777216 is fine, it is simply done here to illustrate how to do it.
+ PROTON_TOTAL_MEMORY = 50000000;
+ var proton = require("qpid-proton");
+
+ var address = "amqp://0.0.0.0";
+ var subject = "UK.WEATHER";
+ var msgtext = "Hello World!";
+ var tracker = null;
+ var running = true;
+
+ var message = new proton.Message();
+ var messenger = new proton.Messenger();
+
+ // This is an asynchronous send, so we can't simply call messenger.put(message)
+ // at the end of the application as we would with a synchronous/blocking
+ // version, as the application would simply exit without actually sending.
+ // The following callback function (and messenger.setOutgoingWindow())
+ // gives us a means to wait until the consumer has received the message before
+ // exiting. The recv.js example explicitly accepts messages it receives.
+ var pumpData = function() {
+ var status = messenger.status(tracker);
+ if (status != proton.Status.PENDING) {
+ if (running) {
+ messenger.stop();
+ running = false;
+ }
+ }
+
+ if (messenger.isStopped()) {
+ message.free();
+ messenger.free();
+ }
+ };
+
+ var args = process.argv.slice(2);
+ if (args.length > 0) {
+ if (args[0] === '-h' || args[0] === '--help') {
+ console.log("Usage: node send.js [options] [message]");
+ console.log("Options:");
+ console.log(" -a <addr> The target address [amqp[s]://domain[/name]] (default " + address + ")");
+ console.log(" -s <subject> The message subject (default " + subject + ")");
+ console.log("message A text string to send.");
+ process.exit(0);
+ }
+
+ for (var i = 0; i < args.length; i++) {
+ var arg = args[i];
+ if (arg.charAt(0) === '-') {
+ i++;
+ var val = args[i];
+ if (arg === '-a') {
+ address = val;
+ } else if (arg === '-s') {
+ subject = val;
+ }
+ } else {
+ msgtext = arg;
+ }
+ }
+ }
+
+ console.log("Address: " + address);
+ console.log("Subject: " + subject);
+ console.log("Content: " + msgtext);
+
+ messenger.on('error', function(error) {console.log(error);});
+ messenger.on('work', pumpData);
+ messenger.setOutgoingWindow(1024); // So we can track status of send message.
+ messenger.start();
+
+ message.setAddress(address);
+ message.setSubject(subject);
+ message.body = msgtext;
+
+ tracker = messenger.put(message);
+} else {
+ console.error("send.js should be run in Node.js");
+}
+
diff --git a/examples/messenger/javascript/server.js b/examples/messenger/javascript/server.js
new file mode 100644
index 0000000..1a9eabb
--- /dev/null
+++ b/examples/messenger/javascript/server.js
@@ -0,0 +1,81 @@
+#!/usr/bin/env node
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Simple server for use with client.js illustrating request/response
+
+// Check if the environment is Node.js and if not log an error and exit.
+if (typeof process === 'object' && typeof require === 'function') {
+ var proton = require("qpid-proton");
+
+ var address = "amqp://~0.0.0.0";
+ var message = new proton.Message();
+ var reply = new proton.Message();
+ var messenger = new proton.Messenger();
+
+ var dispatch = function(request, response) {
+ var subject = request.getSubject();
+ if (subject) {
+ response.setSubject('Re: ' + subject);
+ }
+ response.properties = request.properties
+ console.log("Dispatched " + subject + " " + JSON.stringify(request.properties));
+ };
+
+ var pumpData = function() {
+ while (messenger.incoming()) {
+ var t = messenger.get(message);
+
+ var replyTo = message.getReplyTo();
+ if (replyTo) {
+ console.log(replyTo);
+ reply.setAddress(replyTo);
+ reply.setCorrelationID(message.getCorrelationID());
+ reply.body = message.body;
+ dispatch(message, reply);
+ messenger.put(reply);
+ }
+
+ messenger.accept(t);
+ }
+ };
+
+ var args = process.argv.slice(2);
+ if (args.length > 0) {
+ if (args[0] === '-h' || args[0] === '--help') {
+ console.log("Usage: node server.js <addr> (default " + address + ")");
+ process.exit(0);
+ }
+
+ address = args[0];
+ }
+
+ messenger.setIncomingWindow(1024);
+
+ messenger.on('error', function(error) {console.log(error);});
+ messenger.on('work', pumpData);
+ messenger.recv(); // Receive as many messages as messenger can buffer.
+ messenger.start();
+
+ messenger.subscribe(address);
+} else {
+ console.error("server.js should be run in Node.js");
+}
+
diff --git a/examples/messenger/javascript/spout.js b/examples/messenger/javascript/spout.js
new file mode 100644
index 0000000..16faa8e
--- /dev/null
+++ b/examples/messenger/javascript/spout.js
@@ -0,0 +1,71 @@
+#!/usr/bin/env node
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Check if the environment is Node.js and if not log an error and exit.
+if (typeof process === 'object' && typeof require === 'function') {
+ var proton = require("qpid-proton");
+
+ console.log("spout not implemented yet");
+ process.exit(0);
+
+ var address = "amqp://0.0.0.0";
+ var subject = "UK.WEATHER";
+ var msgtext = "Hello World!";
+ var tracker = null;
+ var running = true;
+
+ var message = new proton.Message();
+ var messenger = new proton.Messenger();
+
+ function pumpData() {
+ var status = messenger.status(tracker);
+ if (status != proton.Status.PENDING) {
+console.log("status = " + status);
+
+ if (running) {
+console.log("stopping");
+ messenger.stop();
+ running = false;
+ }
+ }
+
+ if (messenger.isStopped()) {
+console.log("exiting");
+ message.free();
+ messenger.free();
+ }
+ };
+
+ messenger.on('error', function(error) {console.log(error);});
+ messenger.on('work', pumpData);
+ messenger.setOutgoingWindow(1024);
+ messenger.start();
+
+ message.setAddress(address);
+ message.setSubject(subject);
+
+ message.body = msgtext;
+
+ tracker = messenger.put(message);
+} else {
+ console.error("spout.js should be run in Node.js");
+}
+
diff --git a/examples/messenger/javascript/ws2tcp.js b/examples/messenger/javascript/ws2tcp.js
new file mode 100755
index 0000000..abb78f2
--- /dev/null
+++ b/examples/messenger/javascript/ws2tcp.js
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * ws2tcp.js is a simple node.js library that proxies from a WebSocket to a TCP
+ * Socket or vice versa. It has minimal dependencies - the standard node.js net
+ * library and the ws WebSocket library (npm install ws).
+ * <p>
+ * Two fuctions are exported, ws2tcp proxies from a WebSocket to a TCP Socket and
+ * tcp2ws proxies from a TCP Socket to a WebSocket.
+ * @Author Fraser Adams
+ * @file
+ */
+
+var WebSocket = require('ws');
+var net = require('net');
+
+/**
+ * This function is shared by ws2tcp and tcp2ws and takes care of cleaning up
+ * and closing the WebSocket and Socket when things close down or error.
+ * @param sock the TCP Socket instance we're registering cleanup handlers for.
+ * @param ws the WebSocket instance we're registering cleanup handlers for.
+ */
+var registerCleanupCallbacks = function(sock, ws) {
+ var cleanup = function(sock, ws) {
+ sock.removeAllListeners('close');
+ sock.end();
+ ws.removeAllListeners('close');
+ ws.close();
+ };
+
+ sock.on('close', function() {
+ cleanup(sock, ws);
+ });
+
+ sock.on('error', function (e) {
+ console.log("socket error: " + e.code);
+ cleanup(sock, ws);
+ });
+
+ ws.on('close', function() {
+ cleanup(sock, ws);
+ });
+
+ ws.on('error', function (e) {
+ console.log("websocket error: " + e.code);
+ cleanup(sock, ws);
+ });
+};
+
+/**
+ * This function establishes a proxy that listens on a specified TCP Socket port
+ * and proxies data to a WebSocket on the target host listening on the specified
+ * target port.
+ * @param lport the listen port.
+ * @param thost the target host.
+ * @param tport the target port.
+ * @param subProtocols a string containing a comma separated list of WebSocket sub-protocols.
+ */
+var tcp2ws = function(lport, thost, tport, subProtocols) {
+ var opts = null;
+ if (subProtocols) {
+ // The regex trims the string (removes spaces at the beginning and end,
+ // then splits the string by <any space>,<any space> into an Array.
+ subProtocols = subProtocols.replace(/^ +| +$/g,"").split(/ *, */);
+ opts = {'protocol': subProtocols.toString()};
+ }
+
+ var server = net.createServer(function(sock) {
+ var url = 'ws://' + thost + ':' + tport;
+ var ws = new WebSocket(url, opts);
+ var ready = false;
+ var buffer = [];
+
+ registerCleanupCallbacks(sock, ws);
+
+ sock.on('data', function(data) {
+ if (ready) {
+ ws.send(data);
+ } else {
+ buffer.push(data);
+ }
+ });
+
+ ws.on('open', function () {
+ if (buffer.length > 0) {
+ ws.send(Buffer.concat(buffer));
+ }
+ ready = true;
+ buffer = null;
+ });
+
+ ws.on('message', function(m) {
+ sock.write(m);
+ });
+ });
+ server.listen(lport);
+};
+
+/**
+ * This function establishes a proxy that listens on a specified WebSocket port
+ * and proxies data to a TCP Socket on the target host listening on the specified
+ * target port.
+ * @param lport the listen port.
+ * @param thost the target host.
+ * @param tport the target port.
+ */
+var ws2tcp = function(lport, thost, tport) {
+ var server = new WebSocket.Server({port: lport});
+ server.on('connection', function(ws) {
+ var sock = net.connect(tport, thost);
+ var ready = false;
+ var buffer = [];
+
+ registerCleanupCallbacks(sock, ws);
+
+ ws.on('message', function(m) {
+ if (ready) {
+ sock.write(m);
+ } else {
+ buffer.push(m);
+ }
+ });
+
+ sock.on('connect', function() {
+ if (buffer.length > 0) {
+ sock.write(Buffer.concat(buffer));
+ }
+ ready = true;
+ buffer = null;
+ });
+
+ sock.on('data', function(data) {
+ ws.send(data);
+ });
+ });
+ server.on('error', function(e) {
+ console.log("websocket server error: " + e.code);
+ });
+};
+
+// Export the two proxy functions.
+module.exports.ws2tcp = ws2tcp;
+module.exports.tcp2ws = tcp2ws;
+
diff --git a/proton-c/CMakeLists.txt b/proton-c/CMakeLists.txt
index 96a0540..7872801 100644
--- a/proton-c/CMakeLists.txt
+++ b/proton-c/CMakeLists.txt
@@ -261,6 +261,19 @@
add_subdirectory(bindings)
endif (SWIG_FOUND)
+# Build the JavaScript language binding.
+# This is somewhat different to the other language bindings in that it does not use swig. It uses a C/C++ to
+# JavaScript cross-compiler called emscripten (https://github.com/kripken/emscripten). Emscripten takes C/C++
+# and compiles it into a highly optimisable subset of JavaScript called asm.js (http://asmjs.org/) that can be
+# aggressively optimised and run at near-native speed (usually between 1.5 to 10 times slower than native C/C++).
+option("BUILD_JAVASCRIPT" "Build JavaScript language binding" ON)
+if (BUILD_JAVASCRIPT)
+ find_package(Emscripten)
+ if (EMSCRIPTEN_FOUND)
+ add_subdirectory(bindings/javascript)
+ endif (EMSCRIPTEN_FOUND)
+endif (BUILD_JAVASCRIPT)
+
add_subdirectory(docs/api)
add_subdirectory(docs/man)
add_subdirectory(../tests/tools/apps/c ../tests/tools/apps/c)
diff --git a/proton-c/bindings/javascript/CMakeLists.txt b/proton-c/bindings/javascript/CMakeLists.txt
new file mode 100644
index 0000000..38d039b
--- /dev/null
+++ b/proton-c/bindings/javascript/CMakeLists.txt
@@ -0,0 +1,272 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# This file allows cross-compiling of proton to JavaScript using emscripten
+# (https://github.com/kripken/emscripten). As it is really a cross-compilation
+# (as opposed to a binding like with swig) the approach is rather different and
+# somewhat replicates the main build in the proton-c/CMakeLists.txt using quite
+# a bit of "copy and paste reuse".
+# TODO refactor this file (and proton-c/CMakeLists.txt) to keep the main
+# compilation and cross-compilation consistent.
+
+message(STATUS "Found emscripten, using that to build JavaScript binding")
+
+# Find and install the node.js packages that we might need. We can assume that
+# node.js itself is installed because Emscripten has a dependency on it.
+find_package(NodePackages)
+
+# Describe the target OS we are building to - Emscripten mimics the Linux platform.
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_VERSION 1)
+set(CMAKE_CROSSCOMPILING TRUE)
+
+# Specify the compiler to use for cross-compilation.
+set(CMAKE_C_COMPILER "${EMCC}")
+
+# Don't do compiler autodetection, since we are cross-compiling.
+include(CMakeForceCompiler)
+CMAKE_FORCE_C_COMPILER("${CMAKE_C_COMPILER}" Clang)
+
+# The Proton default build type is RelWithDebInfo, but for JavaScript the C debug
+# mechanism is irrelevant. If Debug is explicitly set we turn off optimisations
+# and don't run the closure compiler so any error/profiling messages are readable.
+if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+ message(STATUS "JavaScript build type is \"Debug\"")
+else()
+ set (CMAKE_BUILD_TYPE Release)
+ message(STATUS "JavaScript build type is \"Release\"")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
+ set(EMSCRIPTEN_LINK_OPTIMISATIONS "-O2 --closure 1")
+endif()
+
+# From this point we should be using emscripten compilation tools.
+message(STATUS "emscripten compilation environment:")
+message(STATUS "CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}")
+
+
+# Set this to the proton-c directory, we're cross-compiling code from there.
+set(PN_PATH ${CMAKE_SOURCE_DIR}/proton-c)
+
+
+# TODO the OpenSSL stuff won't work for emscripten by default. It might well be
+# possible to compile it from source using emscripten (that's the standard practice
+# for libraries with emscripten) see https://github.com/kripken/emscripten/wiki/FAQ
+# "Q. How do I link against system libraries like SDL, boost, etc.?"
+# Though it might be more natural to simply use a TLS protected wss:// WebSocket URL.
+# set(pn_driver_ssl_impl src/ssl/openssl.c)
+# set(SSL_LIB ${OPENSSL_LIBRARIES})
+set(pn_driver_ssl_impl ${PN_PATH}/src/ssl/ssl_stub.c)
+
+# emscripten is Linux like so use the posix driver.
+set(pn_io_impl ${PN_PATH}/src/posix/io.c)
+set(pn_selector_impl ${PN_PATH}/src/posix/selector.c)
+set(pn_driver_impl ${PN_PATH}/src/posix/driver.c)
+
+
+# Generate encodings.h and protocol.h
+# It may be possible to use the ones generated for the main proton-c build but
+# qpid-proton-core has a dependency on the generated files, so I'm not sure if
+# it'll work without a command that can be run as a dependency. Probably best
+# do it this way when cross-compiling - though more copy'n'paste
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/encodings.h
+ COMMAND python ${PN_PATH}/env.py PYTHONPATH=${PN_PATH} python ${PN_PATH}/src/codec/encodings.h.py > ${CMAKE_CURRENT_BINARY_DIR}/encodings.h
+ DEPENDS ${PN_PATH}/src/codec/encodings.h.py
+ )
+
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/protocol.h
+ COMMAND python ${PN_PATH}/env.py PYTHONPATH=${PN_PATH} python ${PN_PATH}/src/protocol.h.py > ${CMAKE_CURRENT_BINARY_DIR}/protocol.h
+ DEPENDS ${PN_PATH}/src/protocol.h.py
+ )
+
+set(COMPILE_WARNING_FLAGS "-Werror -Wall -pedantic-errors -Wno-comment -Wno-warn-absolute-paths")
+
+# Explicitly set PLATFORM_DEFINITIONS to Linux ones for emscripten as we don't
+# want to inadvertently use Windows versions if we happen to be cross-compiling
+# from a Windows platform
+set(PLATFORM_DEFINITIONS "USE_CLOCK_GETTIME;USE_UUID_GENERATE;USE_STRERROR_R;USE_ATOLL")
+
+# The following is largely a copy from the the main proton-c/CMakeLists.txt.
+# The main difference is prefixing paths with ${PN_PATH}/ as we can't use a
+# relative path from this CMakeLists.txt.
+
+set(qpid-proton-platform
+ ${pn_io_impl}
+ ${pn_selector_impl}
+ ${pn_driver_impl}
+ ${PN_PATH}/src/platform.c
+ ${pn_driver_ssl_impl}
+ )
+
+set(qpid-proton-core
+ ${PN_PATH}/src/object/object.c
+ ${PN_PATH}/src/object/list.c
+ ${PN_PATH}/src/object/map.c
+ ${PN_PATH}/src/object/string.c
+ ${PN_PATH}/src/object/iterator.c
+ ${PN_PATH}/src/object/record.c
+
+ ${PN_PATH}/src/util.c
+ ${PN_PATH}/src/url.c
+ ${PN_PATH}/src/error.c
+ ${PN_PATH}/src/buffer.c
+ ${PN_PATH}/src/parser.c
+ ${PN_PATH}/src/scanner.c
+ ${PN_PATH}/src/types.c
+
+ ${PN_PATH}/src/framing/framing.c
+
+ ${PN_PATH}/src/codec/codec.c
+ ${PN_PATH}/src/codec/decoder.c
+ ${PN_PATH}/src/codec/encoder.c
+
+ ${PN_PATH}/src/dispatcher/dispatcher.c
+ ${PN_PATH}/src/engine/engine.c
+ ${PN_PATH}/src/events/event.c
+ ${PN_PATH}/src/transport/autodetect.c
+ ${PN_PATH}/src/transport/transport.c
+ ${PN_PATH}/src/message/message.c
+ ${PN_PATH}/src/sasl/sasl.c
+
+ ${PN_PATH}/src/messenger/messenger.c
+ ${PN_PATH}/src/messenger/subscription.c
+ ${PN_PATH}/src/messenger/store.c
+ ${PN_PATH}/src/messenger/transform.c
+ ${PN_PATH}/src/selectable.c
+
+ ${CMAKE_CURRENT_BINARY_DIR}/encodings.h
+ ${CMAKE_CURRENT_BINARY_DIR}/protocol.h
+ )
+
+set_source_files_properties(
+ ${qpid-proton-core}
+ PROPERTIES
+ COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_LANGUAGE_FLAGS}"
+ )
+
+set_source_files_properties(
+ ${qpid-proton-platform}
+ PROPERTIES
+ COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_PLATFORM_FLAGS}"
+ COMPILE_DEFINITIONS "${PLATFORM_DEFINITIONS}"
+ )
+
+# Compile Proton into LLVM bitcode which will be used later in the linking stage
+# of add_executable for compilation into Javascript.
+add_library(
+ qpid-proton-bitcode SHARED
+
+ ${qpid-proton-core}
+ ${qpid-proton-platform}
+ )
+
+set_target_properties(
+ qpid-proton-bitcode
+ PROPERTIES
+ VERSION "${PN_LIB_SOMAJOR}.${PN_LIB_SOMINOR}"
+ SOVERSION "${PN_LIB_SOMAJOR}"
+ LINK_FLAGS "${CATCH_UNDEFINED}"
+ )
+
+
+# Compile the send-async.c and recv-async.c examples into JavaScript
+add_executable(send-async.js ${PN_PATH}/../examples/messenger/c/send-async.c)
+target_link_libraries(send-async.js qpid-proton-bitcode)
+
+add_executable(recv-async.js ${PN_PATH}/../examples/messenger/c/recv-async.c)
+target_link_libraries(recv-async.js qpid-proton-bitcode)
+
+set_target_properties(
+ send-async.js recv-async.js
+ PROPERTIES
+ COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_PLATFORM_FLAGS}"
+ RUNTIME_OUTPUT_DIRECTORY examples
+ DEPENDS ws
+
+ LINK_FLAGS "-s \"WEBSOCKET_SUBPROTOCOL='AMQPWSB10'\" -${EMSCRIPTEN_LINK_OPTIMISATIONS}"
+ )
+
+# Build the main JavaScript library called proton.js
+add_executable(proton.js binding.c)
+target_link_libraries(proton.js qpid-proton-bitcode)
+
+set_target_properties(
+ proton.js
+ PROPERTIES
+ COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_PLATFORM_FLAGS}"
+
+ # The --memory-init-file 0 stops emscripten emitting a separate memory
+ # initialization file, if this was enabled it makes packaging harder as
+ # applications would expect proton.js.mem to be served too. It's even more
+ # fiddly with node.js packages. This behaviour might be reinstated if the
+ # packaging mechanism improves.
+
+ LINK_FLAGS "-s \"EXPORT_NAME='proton'\" -s \"WEBSOCKET_SUBPROTOCOL='AMQPWSB10'\" ${EMSCRIPTEN_LINK_OPTIMISATIONS} --memory-init-file 0 --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/binding-open.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/module.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/error.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/messenger.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/subscription.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/message.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-uuid.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-symbol.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-described.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-array.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-typed-number.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-long.js --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/data-binary.js --post-js ${CMAKE_CURRENT_SOURCE_DIR}/binding-close.js -s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=\"[]\" -s EXPORTED_FUNCTIONS=\"['_pn_get_version_major', '_pn_get_version_minor', '_pn_bytes', '_pn_error_text', '_pn_code', '_pn_messenger', '_pn_messenger_name', '_pn_messenger_set_blocking', '_pn_messenger_free', '_pn_messenger_errno', '_pn_messenger_error', '_pn_messenger_get_outgoing_window', '_pn_messenger_set_outgoing_window', '_pn_messenger_get_incoming_window', '_pn_messenger_set_incoming_window', '_pn_messenger_start', '_pn_messenger_stop', '_pn_messenger_stopped', '_pn_messenger_subscribe', '_pn_messenger_put', '_pn_messenger_status', '_pn_messenger_buffered', '_pn_messenger_settle', '_pn_messenger_outgoing_tracker', '_pn_messenger_work', '_pn_messenger_recv', '_pn_messenger_receiving', '_pn_messenger_get', '_pn_messenger_incoming_tracker', '_pn_messenger_incoming_subscription', '_pn_messenger_accept', '_pn_messenger_reject', '_pn_messenger_outgoing', '_pn_messenger_incoming', '_pn_messenger_route', '_pn_messenger_rewrite', '_pn_messenger_set_passive', '_pn_messenger_selectable', '_pn_subscription_get_context', '_pn_subscription_set_context', '_pn_subscription_address', '_pn_message', '_pn_message_id', '_pn_message_correlation_id', '_pn_message_free', '_pn_message_errno', '_pn_message_error', '_pn_message_clear', '_pn_message_is_inferred', '_pn_message_set_inferred', '_pn_message_is_durable', '_pn_message_set_durable', '_pn_message_get_priority', '_pn_message_set_priority', '_pn_message_get_ttl', '_pn_message_set_ttl', '_pn_message_is_first_acquirer', '_pn_message_set_first_acquirer', '_pn_message_get_delivery_count', '_pn_message_set_delivery_count', '_pn_message_get_user_id', '_pn_message_set_user_id', '_pn_message_get_address', '_pn_message_set_address', '_pn_message_get_subject', '_pn_message_set_subject', '_pn_message_get_reply_to', '_pn_message_set_reply_to', '_pn_message_get_content_type', '_pn_message_set_content_type', '_pn_message_get_content_encoding', '_pn_message_set_content_encoding', '_pn_message_get_expiry_time', '_pn_message_set_expiry_time', '_pn_message_get_creation_time', '_pn_message_set_creation_time', '_pn_message_get_group_id', '_pn_message_set_group_id', '_pn_message_get_group_sequence', '_pn_message_set_group_sequence', '_pn_message_get_reply_to_group_id', '_pn_message_set_reply_to_group_id', '_pn_message_encode', '_pn_message_decode', '_pn_message_instructions', '_pn_message_annotations', '_pn_message_properties', '_pn_message_body', '_pn_data', '_pn_data_free', '_pn_data_error', '_pn_data_errno', '_pn_data_clear', '_pn_data_rewind', '_pn_data_next', '_pn_data_prev', '_pn_data_enter', '_pn_data_exit', '_pn_data_lookup', '_pn_data_narrow', '_pn_data_widen', '_pn_data_type', '_pn_data_encode', '_pn_data_decode', '_pn_data_put_list', '_pn_data_put_map', '_pn_data_put_array', '_pn_data_put_described', '_pn_data_put_null', '_pn_data_put_bool', '_pn_data_put_ubyte', '_pn_data_put_byte', '_pn_data_put_ushort', '_pn_data_put_short', '_pn_data_put_uint', '_pn_data_put_int', '_pn_data_put_char', '_pn_data_put_ulong', '_pn_data_put_long', '_pn_data_put_timestamp', '_pn_data_put_float', '_pn_data_put_double', '_pn_data_put_decimal32', '_pn_data_put_decimal64', '_pn_data_put_decimal128', '_pn_data_put_uuid', '_pn_data_put_binary', '_pn_data_put_string', '_pn_data_put_symbol', '_pn_data_get_list', '_pn_data_get_map', '_pn_data_get_array', '_pn_data_is_array_described', '_pn_data_get_array_type', '_pn_data_is_described', '_pn_data_is_null', '_pn_data_get_bool', '_pn_data_get_ubyte', '_pn_data_get_byte', '_pn_data_get_ushort', '_pn_data_get_short', '_pn_data_get_uint', '_pn_data_get_int', '_pn_data_get_char', '_pn_data_get_ulong', '_pn_data_get_long', '_pn_data_get_timestamp', '_pn_data_get_float', '_pn_data_get_double', '_pn_data_get_decimal32', '_pn_data_get_decimal64', '_pn_data_get_decimal128', '_pn_data_get_uuid', '_pn_data_get_binary', '_pn_data_get_string', '_pn_data_get_symbol', '_pn_data_copy', '_pn_data_format', '_pn_data_dump', '_pn_selectable_readable', '_pn_selectable_capacity', '_pn_selectable_writable', '_pn_selectable_pending', '_pn_selectable_is_terminal', '_pn_selectable_fd', '_pn_selectable_free']\""
+ )
+
+# This command packages up the compiled proton.js into a node.js package called
+# qpid-proton and copies it to the <proton>/node_modules directory. This allows
+# the node.js test and example programs to do proton = require("qpid-proton");
+add_custom_command(
+ TARGET proton.js
+ COMMAND ${CMAKE_COMMAND}
+ -E copy_directory
+ ${CMAKE_CURRENT_SOURCE_DIR}/qpid-proton
+ ${PROJECT_SOURCE_DIR}/node_modules/qpid-proton
+ COMMAND ${CMAKE_COMMAND}
+ -E copy
+ ${CMAKE_CURRENT_BINARY_DIR}/proton.js
+ ${PROJECT_SOURCE_DIR}/node_modules/qpid-proton/lib
+ COMMENT "Building qpid-proton package for node.js"
+)
+
+# The cleanall target removes the qpid-proton package from <proton>/node_modules
+add_custom_target(
+ cleanall
+ COMMAND echo "CLEAN NODE MODULES"
+ COMMAND ${CMAKE_COMMAND}
+ -E remove_directory
+ ${PROJECT_SOURCE_DIR}/node_modules/qpid-proton
+)
+
+# If the docs target is specified and the jsdoc3 package for node.js has been
+# installed then build the JavaScript API documentation.
+if (NODE_JSDOC_FOUND)
+ set(JSDOC_EXE ${PROJECT_SOURCE_DIR}/node_modules/.bin/jsdoc)
+
+ message(STATUS "Documentation Enabled. Using ${JSDOC_EXE} to build JavaScript docs")
+ add_custom_target(docs-js COMMAND ${JSDOC_EXE}
+ -d ${CMAKE_CURRENT_BINARY_DIR}/html
+ ${CMAKE_CURRENT_SOURCE_DIR}/module.js
+ ${CMAKE_CURRENT_SOURCE_DIR}/error.js
+ ${CMAKE_CURRENT_SOURCE_DIR}/messenger.js
+ ${CMAKE_CURRENT_SOURCE_DIR}/subscription.js
+ ${CMAKE_CURRENT_SOURCE_DIR}/message.js
+ ${CMAKE_CURRENT_SOURCE_DIR}/data.js
+ ${CMAKE_CURRENT_SOURCE_DIR}/data-uuid.js
+ ${CMAKE_CURRENT_SOURCE_DIR}/data-symbol.js
+ ${CMAKE_CURRENT_SOURCE_DIR}/data-described.js
+ ${CMAKE_CURRENT_SOURCE_DIR}/data-array.js
+ ${CMAKE_CURRENT_SOURCE_DIR}/data-typed-number.js
+ ${CMAKE_CURRENT_SOURCE_DIR}/data-long.js
+ ${CMAKE_CURRENT_SOURCE_DIR}/data-binary.js)
+ add_dependencies(docs docs-js)
+endif (NODE_JSDOC_FOUND)
+
diff --git a/proton-c/bindings/javascript/LICENSE b/proton-c/bindings/javascript/LICENSE
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/proton-c/bindings/javascript/LICENSE
@@ -0,0 +1,203 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/proton-c/bindings/javascript/README b/proton-c/bindings/javascript/README
new file mode 100644
index 0000000..8bfde56
--- /dev/null
+++ b/proton-c/bindings/javascript/README
@@ -0,0 +1,426 @@
+Qpid Proton JavaScript Language Bindings
+========================================
+
+The code contained herein provides JavaScript language bindings for working
+with the Qpid Proton AMQP 1.0 protocol engine and messenger library.
+
+Important Note - Modern Browser Needed
+======================================
+
+The JavaScript binding requires ArrayBuffer/TypedArray and WebSocket support.
+Both of these are available in most "modern" browser versions. The author has
+only tried running on FireFox and Chrome, though recent Safari, Opera and IE10+
+*should* work too - YMMV. It might be possible to polyfill for older browsers
+but the author hasn't tried this.
+
+Important Note - WebSocket Transport!!!
+=======================================
+
+Before going any further it is really important to realise that the JavaScript
+bindings to Proton are somewhat different to the bindings for other languages
+because of the restrictions of the execution environment. In particular it is
+very important to note that the JavaScript bindings by default use a WebSocket
+transport and not a TCP transport, so whilst it's possible to create Server style
+applications that clients can connect to (e.g. recv.js and send.js) note that:
+
+JavaScript clients cannot *directly* talk to "normal" AMQP applications such as
+qpidd or (by default) the Java Broker because they use a standard TCP transport.
+
+This is a slightly irksome issue, but there's no getting away from it because
+it's a security restriction imposed by the browser environment.
+
+
+At the moment even for Node.js we are limited to using a WebSocket transport, but
+it is on the author's "TODO" list to look at providing a means to use either a
+WebSocket transport or a native TCP transport (via node's net library). It should
+also be possible to use native TCP for Chrome "packaged apps", but again this is
+only on the TODO list so if you want to talk to a "normal" AMQP application you
+must live with the WebSocket constraints.
+
+Option 1. proxy from WebSockets to TCP sockets. The application
+<proton>/examples/messenger/javascript/proxy.js
+is a simple Node.js WebSocket<->TCP Socket proxy, simply doing:
+
+node proxy.js
+
+will stand up a proxy listening by default on WebSocket port 5673 and forwarding
+to TCP port 5672 (this is configurable, for options do: node proxy.js -h)
+
+Rather than using a stand-alone proxy it is possible to have applications stand
+up their own proxy (where lport = listen port, thost = target host and
+tport = target port):
+
+var proxy = require('./ws2tcp.js');
+proxy.ws2tcp(lport, thost, tport);
+
+For talking to the C++ broker unfortunately proxying is currently the only option
+as qpidd does not have a WebSocket transport.
+
+Option 2. The Java Broker's WebSocket transport.
+Recent releases of the Qpid Java Broker provide a native WebSocket transport which
+means that the JavaScript binding can talk to it with no additional requirements.
+It is necessary to configure the Java Broker as the WebSocket transport is not
+enabled by default. In <qpid-work>/config.json in the "ports" array you need to add:
+
+{
+ "authenticationProvider" : "passwordFile",
+ "name" : "AMQP-WS",
+ "port" : "5673",
+ "transports" : [ "WS" ]
+}
+
+This sets the JavaBroker to listen on WebSocket port 5673 similar to the proxy.
+
+
+One gotcha that still bites the author *** DO NOT FORGET *** that you will be
+using ports that you are not used to!! If you are not connecting check that the
+proxy is running and that you are specifying the right ports. I still mess up :-(
+
+WebRTC Transport
+================
+
+A WebRTC Transport is supported by emscripten, though the author has not tried it.
+If you wan to play with this you are very much on your own at the moment YMMV.
+
+This is configured by adding to the LINK_FLAGS in CMakeLists.txt
+-s \"SOCKET_WEBRTC=1\"
+
+ /* WebRTC sockets supports several options on the Module object.
+
+ * Module['host']: true if this peer is hosting, false otherwise
+ * Module['webrtc']['broker']: hostname for the p2p broker that this peer should use
+ * Module['webrtc']['session']: p2p session for that this peer will join, or undefined if this peer is hosting
+ * Module['webrtc']['hostOptions']: options to pass into p2p library if this peer is hosting
+ * Module['webrtc']['onpeer']: function(peer, route), invoked when this peer is ready to connect
+ * Module['webrtc']['onconnect']: function(peer), invoked when a new peer connection is ready
+ * Module['webrtc']['ondisconnect']: function(peer), invoked when an existing connection is closed
+ * Module['webrtc']['onerror']: function(error), invoked when an error occurs
+ */
+
+If you wanted to play with these you'd likely have to tweak the module.js code in
+<proton>/proton-c/bindings/javascript
+
+The emscripten documentation is a bit light on the WebRTC Transport too, though
+
+emscripten/tests/test_sockets.py
+emscripten/tests/sockets/webrtc_host.c
+emscripten/tests/sockets/webrtc_peer.c
+emscripten/tests/sockets/p2p/broker/p2p-broker.js
+emscripten/tests/sockets/p2p/client/p2p-client.js
+
+Look like they provide a starting point.
+Very much TODO......
+
+
+Creating The Bindings
+=====================
+
+To generate the JavaScript bindings we actually cross-compile from proton-c
+
+You will need to have emscripten (https://github.com/kripken/emscripten) installed
+to do the cross-compilation and in addition you will require a few things that
+emscripten itself depends upon.
+
+http://kripken.github.io/emscripten-site/docs/building_from_source/index.html#installing-from-source
+http://kripken.github.io/emscripten-site/docs/building_from_source/toolchain_what_is_needed.html
+provide instructions for installing emscripten and the "fastcomp" LLVM backend.
+This approach lets users use the "bleeding edge" version of emscripten on the
+"incoming" branch (pretty much analogous to building qpid/proton off svn trunk).
+This is the approach that the author of the JavaScript Bindings tends to use.
+
+
+http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html
+provides some fairly easy to follow instructions for getting started on several
+platforms the main dependencies are as follows (on Windows the SDK includes these):
+
+* The Emscripten code, from github (git clone git://github.com/kripken/emscripten.git).
+* LLVM with Clang. Emscripten uses LLVM and Clang, but at the moment the JavaScript
+ back-end for LLVM is off on a branch so you can't use a stock LLVM/Clang.
+ http://kripken.github.io/emscripten-site/docs/building_from_source/LLVM-Backend.html
+ http://kripken.github.io/emscripten-site/docs/building_from_source/building_fastcomp_manually_from_source.html#building-fastcomp-from-source
+ has lots of explanation and some easy to follow instructions for downloading
+ and building fastcomp
+* Node.js (0.8 or above; 0.10.17 or above to run websocket-using servers in node)
+* Python 2.7.3
+* Java is required in order to use the Closure Compiler to minify the code.
+
+
+If you haven't run Emscripten before it's a good idea to have a play with the
+tutorial here:
+http://kripken.github.io/emscripten-site/docs/getting_started/Tutorial.html
+
+
+
+when you are all set up with emscripten and have got the basic tests in the
+tutorial running building Proton should be simple, simply go to the Proton root
+directory and follow the main instructions in the README there, in precis (from
+the root directory) it's:
+
+ mkdir build
+ cd build
+ cmake ..
+ make
+
+and you should be all set, to test it's all working do (from the build directory):
+ cd proton-c/bindings/javascript/examples
+
+ node recv-async.js
+
+in one window and
+
+ node send-async.js
+
+in another.
+
+These examples are actually JavaScript applications that have been directly
+compiled from recv-async.c and send-async.c in <proton>/examples/messenger/c
+if you'd prefer to write applications in C and compile them to JavaScript that
+is a perfectly valid approach and these examples provide a reasonable starting
+point for doing so.
+
+Documentation
+=============
+
+When you've successfully got a working build do:
+
+ make docs
+
+Which will make all of the proton documentation including the JavaScript docs.
+If successful the JSDoc generated documentation should be found here:
+
+<proton>/build/proton-c/bindings/javascript/html/index.html
+
+
+Using "native" JavaScript
+=========================
+
+The examples in <proton>/examples/messenger/javascript are the best starting point.
+
+In practice the examples follow a fairly similar pattern to the Python bindings
+the most important thing to bear in mind though is that JavaScript is completely
+asynchronous/non-blocking, which can catch the unwary.
+
+An application follows the following (rough) steps:
+
+1. (optional) Set the heap size.
+It's important to realise that most of the library code is compiled C code and
+the runtime uses a "virtual heap" to support the underlying malloc/free. This is
+implemented internally as an ArrayBuffer with a default size of 16777216.
+
+To allocate a larger heap an application must set the PROTON_TOTAL_MEMORY global.
+In Node.js this would look like (see send.js):
+PROTON_TOTAL_MEMORY = 50000000; // Note no var - it needs to be global.
+
+In a browser it would look like (see send.html):
+<script type="text/javascript">PROTON_TOTAL_MEMORY = 50000000;</script>
+
+2. Load the library and create a message and messenger.
+In Node.js this would look like (see send.js):
+var proton = require("qpid-proton");
+var message = new proton.Message();
+var messenger = new proton.Messenger();
+
+In a browser it would look like (see send.html):
+<script type="text/javascript" src="../../../node_modules/qpid-proton/lib/proton.js"></script>
+
+<script type="text/javascript">
+var message = new proton.Message();
+var messenger = new proton.Messenger();
+....
+
+3. Set up event handlers as necessary.
+
+messenger.on('error', <error callback>);
+messenger.on('work', <work callback>);
+messenger.on('subscription', <subscription callback>);
+
+
+The work callback is triggered on WebSocket events, so in general you would use
+this to send and receive messages, for example in recv.js we have:
+
+var pumpData = function() {
+ while (messenger.incoming()) {
+ var t = messenger.get(message);
+
+ console.log("Address: " + message.getAddress());
+ console.log("Subject: " + message.getSubject());
+
+ // body is the body as a native JavaScript Object, useful for most real cases.
+ //console.log("Content: " + message.body);
+
+ // data is the body as a proton.Data Object, used in this case because
+ // format() returns exactly the same representation as recv.c
+ console.log("Content: " + message.data.format());
+
+ messenger.accept(t);
+ }
+};
+
+messenger.on('work', pumpData);
+
+
+The subscription callback is triggered when the address provided in a call to
+messenger.subscribe(<address>);
+
+Gets resolved. An example of its usage can be found in qpid-config.js which is
+a fully functioning and complete port of the python qpid-config tool. It also
+illustrates how to do asynchronous request/response based applications.
+
+Aside from the asynchronous aspects the rest of the API is essentially the same
+as the Python binding aside from minor things such as camel casing method names etc.
+
+Serialisation/Deserialisation, Types etc.
+=========================================
+
+The JavaScript binding tries to be as simple, intuitive and natural as possible
+so when sending a message all native JavaScript types including Object literals
+and Arrays are transparently supported, for example.
+
+var message = new proton.Message();
+message.setAddress('amqp://localhost');
+message.setSubject('UK.NEWS');
+message.body = ['Rod', 'Jane', 'Freddy', {cat: true, donkey: 'hee haw'}];
+
+
+The main thing to bear in mind is that (particularly for sending messages) we
+may need to use "adapters" to make sure values are correctly interpreted and
+encoded to the correct type in the AMQP type system. This is especially important
+when interoperating with a peer written in a strongly typed language (C/C++/Java).
+
+Some examples of available types follow:
+
+// UUID
+message.body = new proton.Data.Uuid();
+
+// AMQP Symbol
+message.body = new proton.Data.Symbol("My Symbol");
+
+// Binary data (created from a gibberish String in this case).
+message.body = new proton.Data.Binary("Monkey Bathпогромзхцвбнм");
+
+// Binary data (Get a Uint8Array view of the data and directly access that).
+message.body = new proton.Data.Binary(4);
+var buffer = message.body.getBuffer();
+buffer[0] = 65;
+buffer[1] = 77;
+buffer[2] = 81;
+buffer[3] = 80;
+
+// Binary Data (Created from an Array, you can use an ArrayBuffer/TypedArray too).
+message.body = new proton.Data.Binary([65, 77, 81, 80]);
+
+
+Note that the implementation of proton.Data.Binary tries to minimise copying so
+it accesses the internal emscripten heap *directly* this requires memory management
+which is mostly handled transparently, but users need to be aware that the
+underlying memory is "owned" by the Message Object, so if Binary data needs to
+be maintained after the next call to messenger.get(message); it must be
+*explicitly* copied. For more detail do "make docs" and see:
+<proton>/build/proton-c/bindings/javascript/html/proton.Data.Binary.html
+
+
+// AMQP Described (value, descriptor)
+message.body = new proton.Data.Described('persian, 'com.cheezburger.icanhas');
+
+// AMQP Timestamp maps to native JavaScript Date.
+message.body = new Date();
+
+// Various AMQP Array examples.
+message.body = new proton.Data.Array('INT', [1, 3, 5, 7], "odd numbers");
+message.body = new proton.Data.Array('UINT', [1, 3, 5, 7], "odd");
+message.body = new proton.Data.Array('ULONG', [1, 3, 5, 7], "odd");
+message.body = new proton.Data.Array('FLOAT', [1, 3, 5, 7], "odd");
+message.body = new proton.Data.Array('STRING', ["1", "3", "5", "7"], "odd");
+
+// A JavaScript TypedArray will map directly to and from an AMQP Array of the
+// appropriate type (Internally sets a descriptor of 'TypedArray').
+message.body = new Uint8Array([1, 3, 5, 7]);
+
+// UUID Array
+message.body = new proton.Data.Array('UUID', [new proton.Data.Uuid(), new proton.Data.Uuid(), new proton.Data.Uuid(), new proton.Data.Uuid()], "unique");
+
+// Null
+message.body = null;
+
+// Boolean
+message.body = true;
+
+// Native JavaScript Array maps to an AMQP List
+message.body = ['Rod', 'Jane', 'Freddy'];
+
+// Native JavaScript Object maps to an AMQP Map
+message.body = ['Rod', 'Jane', 'Freddy', {cat: true, donkey: 'hee haw'}];
+
+// JavaScript only has a "number" type so the binding provides "decorator"
+// methods added to the JavaScript Number class. To access this from number
+// primitives it is necessary to either use braces or use a "double dot" so that
+// the interpreter can disambiguate from a simple decimal point. The binding will
+// attempt to use the correct type such that message.body = 2147483647; would be
+// sent as an AMQP integer, but because of the way JavaScript coerces integers
+// message.body = 2147483647.0; would also be sent as an AMQP integer because
+// 2147483647.0 gets transparently conveted to 2147483647 by the interpreter, so
+// to explicitly send this as an AMQP float we'd need to do:
+// message.body = 2147483647.0.float();
+
+// Some more number examples:
+message.body = 66..char(); // char - note double dot. (66).char(); works too.
+message.body = 2147483647; // int
+message.body = -2147483649; // long
+message.body = 12147483649; // long
+message.body = (12147483649).long(); // long
+message.body = (17223372036854778000).ulong(); // ulong
+message.body = (121474.836490).float(); // float
+message.body = 12147483649.0.float(); // float
+message.body = (4294967296).uint(); // uint
+message.body = (255).ubyte(); // ubyte
+
+Note too that floats are subject to a loss of precision
+
+
+Fortunately most of these quirks only affect serialisation.when the binding
+receives a message it will attempt to decode it into the most "natural" native
+JavaScript type.
+
+
+One additional decoding "quirk" can be caused by C++ qpid::messaging clients. It
+is unfortunately quite common for C++ clients to incorrectly encode Strings as
+AMQP Binary by neglecting to provide an encoding. The QMF Management Agent is one
+such culprit. This is a bit of a pain, especially because of proton.Data.Binary
+memory management quirks and having to remember to explicitly copy the data
+on each call to messenger.get(message); In order to cater for this an overloaded
+messenger.get(message, true); has been provided. Setting the second parameter to
+true forces any received Binary payloads to be decoded as Strings. If you know
+that producers might behave in this way and you are not expecting any "real"
+Binary data from the producer this convenience mechanism results in nice clean
+JavaScript Objects being received and is extremely useful for things like QMF.
+
+JSON
+====
+
+As well as allowing native JavaScript Objects and Arrays to be transparently
+serialised and deserialised via the AMQP type system it is also possible to
+serialise/deserialise as JSON.
+
+message.setAddress('amqp://localhost');
+message.setContentType('application/json');
+message.body = {array: [1, 2, 3, 4], object: {name: "John Smith", age: 65}};
+
+On the wire the above will be encoded as an opaque binary in an AMQP data section
+but will be deserialised into a JavaScript Object in exactly the same was as the
+previous examples that use the AMQP type system.
+
+SUPPORT
+=======
+
+To report bugs in the bindings, or to request an enhancement, please file
+a tracker request:
+
+ https://issues.apache.org/jira/browse/PROTON
+
+The main support channel is the qpid-users mailing list, see:
+
+ http://qpid.apache.org/discussion.html#mailing-lists
+
+You can also directly interact with the development team and other users
+in the #qpid channel on irc.freenode.net.
+
diff --git a/proton-c/bindings/javascript/TODO b/proton-c/bindings/javascript/TODO
new file mode 100644
index 0000000..092ee2e
--- /dev/null
+++ b/proton-c/bindings/javascript/TODO
@@ -0,0 +1,49 @@
+Qpid Proton JavaScript Language Bindings TODO List
+==================================================
+
+Network code is currently limited to a WebSocket transport, including for Node.js
+It would be good to allow a configurable transport so that Node.js and Chrome
+packaged apps could use native TCP sockets.
+
+The JavaScript binding is pure JavaScript, that has been trans-compiled from C
+to JavaScript using emscripten. This allows the same code to be used in a browser
+and Node.js, but it potentially has a performance penalty in Node.js. An alternative
+for Node.js might be to build a SWIG binding (recent versions of SWIG support
+JavaScript). This should be viewed as a complementary not competing approach as
+it would only work for environments like Node.js and definitely *not* browser
+environments which clearly require pure JavaScript.
+
+Optimisation are enabled for compiling and linking but there hasn't been any
+profiling done yet. The binding code *shouldn't* be the bottleneck but it's
+always possible that I messed up.
+
+Error handling is nowhere near as good as it should be, though this is mostly
+because Messenger itself is a bit lacking on the error handling/recovery front.
+
+Although described as "Proton" this is currently a JavaScript binding for Messenger
+and associated Message & Data classes. There has been some talk on the user list
+of an alternative reactive API based on proton Engine. This might ultimately be
+a better fit for JavaScript but it is very much in its infancy and I haven't
+really looked at it yet.
+
+proton-j seems to use hawt-dispatch, which is modelled after Grand Central
+Dispatch so I need to work out what it's using it do do and whether there are
+parallels in proton-c
+
+Although the WebSocket transport uses the sub-protocol 'AMQPWSB10' as specified
+in http://docs.oasis-open.org/amqp-bindmap/amqp-wsb/v1.0/amqp-wsb-v1.0.html
+section 2.1 is is not technically compliant with the spec. as the AMQP data is
+created by the proton-c code, which produces a data-stream for the TCP transport
+whereas the WebSocket spec. seems to want to make use of the fact that WebSocket
+is a frame based transport (whereas TCP is not). This is quite hard to resolve
+as the binding simply sends the contents of the octet buffer created by proton
+over the transport and thus to make this binding compliant with the spec. would
+require a change to the underlying proton-c code! It is possible that this may be
+done in future as any SCTP would require the ability to push AMQP frames too.
+In the mean time fortunately the Java Broker WebSocket transport is actually
+tolerant of this off-spec. behaviour. My personal view is that both approaches
+should be valid and in particular using the standard TCP framing means that it
+is straightforward to create WebSocket<->TCP proxies, which is useful given that
+only the Java Broker currently has a native WebSocket transport.
+
+
diff --git a/proton-c/bindings/javascript/binding-close.js b/proton-c/bindings/javascript/binding-close.js
new file mode 100644
index 0000000..07b68c2
--- /dev/null
+++ b/proton-c/bindings/javascript/binding-close.js
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * These values are essentially constants sitting in the proton namespace
+ * that is to say they will be exported via:
+ * proton.VERSION_MAJOR
+ * proton.VERSION_MINOR
+ * We have to set them after pn_get_version_major/pn_get_version_minor have been
+ * defined so we must do it here in binding-close.js as it's a --post-js block.
+ */
+Module['VERSION_MAJOR'] = _pn_get_version_major();
+Module['VERSION_MINOR'] = _pn_get_version_minor();
+
+})(); // End of self calling lambda used to wrap library.
+
diff --git a/proton-c/bindings/javascript/binding-open.js b/proton-c/bindings/javascript/binding-open.js
new file mode 100644
index 0000000..40e1b12
--- /dev/null
+++ b/proton-c/bindings/javascript/binding-open.js
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+(function() { // Start of self-calling lambda used to avoid polluting global namespace.
diff --git a/proton-c/bindings/javascript/binding.c b/proton-c/bindings/javascript/binding.c
new file mode 100644
index 0000000..ecc65dc
--- /dev/null
+++ b/proton-c/bindings/javascript/binding.c
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is largely just a stub, we're actually creating a JavaScript library
+ * rather than an executable so most of the work is done at the link stage.
+ * We do however need to link the libqpid-proton-bitcode.so with *something* and
+ * this is it. This file also provides a way to pass any global variable or
+ * #defined values to the JavaScript binding by providing wrapper functions.
+ */
+#include <stdio.h>
+#include <proton/version.h>
+
+// To access #define values in JavaScript we need to wrap them in a function.
+int pn_get_version_major() {return PN_VERSION_MAJOR;}
+int pn_get_version_minor() {return PN_VERSION_MINOR;}
+
diff --git a/proton-c/bindings/javascript/data-array.js b/proton-c/bindings/javascript/data-array.js
new file mode 100644
index 0000000..6f9776d
--- /dev/null
+++ b/proton-c/bindings/javascript/data-array.js
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*****************************************************************************/
+/* */
+/* proton.Data.Array */
+/* */
+/*****************************************************************************/
+
+/**
+ * TODO make this behave more like a native JavaScript Array: http://www.bennadel.com/blog/2292-extending-javascript-arrays-while-keeping-native-bracket-notation-functionality.htm
+ * Create a proton.Data.Array.
+ * @classdesc
+ * This class represents an AMQP Array.
+ * @constructor proton.Data.Array
+ * @param {string|number} type the type of the Number either as a string or number.
+ * Stored internally as a string corresponding to one of the TypeNames.
+ * @param {Array|TypedArray} elements the Native JavaScript Array or TypedArray that we wish to serialise.
+ * @param {object} descriptor an optional object describing the type.
+ */
+Data['Array'] = function(type, elements, descriptor) { // Data.Array Constructor.
+ // This block caters for an empty Array or a Described empty Array.
+ if (arguments.length < 2) {
+ descriptor = type;
+ type = 'NULL';
+ elements = [];
+ }
+
+ this['type'] = (typeof type === 'number') ? Data['TypeNames'][type] : type;
+ this['elements'] = elements;
+ this['descriptor'] = descriptor;
+};
+
+/**
+ * @method toString
+ * @memberof! proton.Data.Array#
+ * @returns {string} the String form of a {@link proton.Data.Array}.
+ */
+Data['Array'].prototype.toString = function() {
+ var descriptor = (this['descriptor'] == null) ? '' : ':' + this['descriptor'];
+ return this['type'] + 'Array' + descriptor + '[' + this['elements'] + ']';
+};
+
+/**
+ * @method valueOf
+ * @memberof! proton.Data.Array#
+ * @returns {Array} the elements of the {@link proton.Data.Array}.
+ */
+Data['Array'].prototype.valueOf = function() {
+ return this['elements'];
+};
+
+/**
+ * Compare two instances of proton.Data.Array for equality. N.B. this method
+ * compares the value of every Array element so its performance is O(n).
+ * @method equals
+ * @memberof! proton.Data.Array#
+ * @param {proton.Data.Array} rhs the instance we wish to compare this instance with.
+ * @returns {boolean} true iff the two compared instances are equal.
+ */
+Data['Array'].prototype['equals'] = function(rhs) {
+ if (rhs instanceof Data['Array'] &&
+ // Check the string value of the descriptors.
+ (('' + this['descriptor']) === ('' + rhs['descriptor'])) &&
+ (this['type'] === rhs['type'])) {
+ var elements = this['elements'];
+ var relements = rhs['elements'];
+ var length = elements.length;
+ if (length === relements.length) {
+ for (var i = 0; i < length; i++) {
+ if (elements[i].valueOf() !== relements[i].valueOf()) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+};
+
diff --git a/proton-c/bindings/javascript/data-binary.js b/proton-c/bindings/javascript/data-binary.js
new file mode 100644
index 0000000..27bac59
--- /dev/null
+++ b/proton-c/bindings/javascript/data-binary.js
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*****************************************************************************/
+/* */
+/* proton.Data.Binary */
+/* */
+/*****************************************************************************/
+
+/**
+ * Create a proton.Data.Binary. This constructor takes one or two parameters.
+ * The first parameter may serve different purposes depending on its type;
+ * If value is a number then it represents the size of the Binary data buffer,
+ * if it is a string then we copy the string to the buffer, if it is an Array
+ * or a TypedArray then we copy the data to the buffer. The optional second
+ * parameter is a pointer to the data in an internal data store. If start is
+ * not specified then the number of bytes specified in the first parameter
+ * will be allocated in the internal data store and start will point to the
+ * start of that block of data.
+ * @classdesc
+ * This class represents an AMQP Binary type. This class allows us to create and
+ * use raw binary data and map it efficiently between JavaScript client code and
+ * the underlying implementation, where all data is managed on a "virtual heap".
+ * <p>
+ * Client applications should generally not have to care about memory management
+ * as, for most common use cases, client applications would "transfer ownership"
+ * to a "container" which would then "own" the underlying data and free the data
+ * held by the {@link proton.Data.Binary}.
+ * <p>
+ * As an example one common use-case would be where client application creates a
+ * {@link proton.Data.Binary} specifying the required size. It would usually then
+ * call getBuffer() to access the underlying Uint8Array. At this point the client
+ * "owns" the data and so would have to call free() if it did nothing more with
+ * the Binary, however when {@link proton.Data.putBINARY} is called the ownership
+ * of the raw data on the virtual heap transfers from the Binary to the Data and
+ * the client no longer needs to call free() itself. In this case the putBINARY()
+ * call transfers ownership and can then safely call free() on the Binary.
+ * <p>
+ * Conversely a common use-case when receiving data is where a Binary may be
+ * created by {@link proton.Data#getBINARY}. In this case the Binary is simply a
+ * "view" onto the bytes owned by the Data instance. A client application can
+ * safely access the bytes from the view, but if it wishes to use the bytes beyond
+ * the scope of the Data instance (e.g. after the next {@link proton.Messenger#get}
+ * call then the client must explicitly *copy* the bytes into a new buffer, for
+ * example via copyBuffer().
+ * <p>
+ * Most of the {@link proton.Data} methods that take {@link proton.Data.Binary}
+ * as a parameter "consume" the underlying data and take responsibility for
+ * freeing it from the heap e.g. {@link proton.Data#putBINARY}, {@link proton.Data#decode}.
+ * For the methods that return a {@link proton.Data.Binary} the call to
+ * {@link proton.Data#getBINARY}, which is the most common case, returns a Binary
+ * that has a "view" of the underlying data that is actually owned by the Data
+ * instance and thus doesn't need to be explicitly freed on the Binary. The call
+ * to {@link proton.Data#encode} however returns a Binary that represents a *copy*
+ * of the underlying data, in this case (like a client calling new proton.Data.Binary)
+ * the client takes responsibility for freeing the data, unless of course it is
+ * subsequently passed to a method that will consume the data (putBINARY/decode).
+ * @constructor proton.Data.Binary
+ * @param {(number|string|Array|TypedArray)} value If value is a number then it
+ * represents the size of the Binary data buffer, if it is a string then
+ * we copy the string to the buffer, if it is an Array or a TypedArray
+ * then we copy the data to the buffer. N.B. although convenient do bear
+ * in mind that using a mechanism other than constructing with a simple
+ * size will result in some form of additional data copy.
+ * @param {number} start an optional pointer to the start of the Binary data buffer.
+ */
+Data['Binary'] = function(value, start) { // Data.Binary Constructor.
+ /**
+ * If the start pointer is specified then the underlying binary data is owned
+ * by another object, so we set the call to free to be a null function. If
+ * the start pointer is not passed then we allocate storage of the specified
+ * size on the emscripten heap and set the call to free to free the data from
+ * the emscripten heap.
+ */
+ var size = value;
+ if (start) {
+ this['free'] = function() {};
+ } else { // Create Binary from Array, ArrayBuffer or TypedArray.
+ var hasArrayBuffer = (typeof ArrayBuffer === 'function');
+ if (Data.isArray(value) ||
+ (hasArrayBuffer && value instanceof ArrayBuffer) ||
+ (value.buffer && hasArrayBuffer && value.buffer instanceof ArrayBuffer)) {
+ value = new Uint8Array(value);
+ size = value.length;
+ start = _malloc(size); // Allocate storage from emscripten heap.
+ Module['HEAPU8'].set(value, start); // Copy the data to the emscripten heap.
+ } else if (Data.isString(value)) { // Create Binary from native string
+ value = unescape(encodeURIComponent(value)); // Create a C-like UTF representation.
+ size = value.length;
+ start = _malloc(size); // Allocate storage from emscripten heap.
+ for (var i = 0; i < size; i++) {
+ setValue(start + i, value.charCodeAt(i), 'i8', 1);
+ }
+ } else { // Create unpopulated Binary of specified size.
+ // If the type is not a number by this point then an unrecognised data
+ // type has been passed so we create a zero length Binary.
+ size = Data.isNumber(size) ? size : 0;
+ start = _malloc(size); // Allocate storage from emscripten heap.
+ }
+ this['free'] = function() {
+ _free(this.start);
+ this.size = 0;
+ this.start = 0;
+ // Set free to a null function to prevent possibility of a "double free".
+ this['free'] = function() {};
+ };
+ }
+
+ this.size = size;
+ this.start = start;
+};
+
+/**
+ * Get a Uint8Array view of the data. N.B. this is just a *view* of the data,
+ * which will go out of scope on the next call to {@link proton.Messenger.get}. If
+ * a client wants to retain the data then copy should be used to explicitly
+ * create a copy of the data which the client then owns to do with as it wishes.
+ * @method getBuffer
+ * @returns {Uint8Array} a new Uint8Array view of the data.
+ * @memberof! proton.Data.Binary#
+ */
+Data['Binary'].prototype['getBuffer'] = function() {
+ return new Uint8Array(HEAPU8.buffer, this.start, this.size);
+};
+
+/**
+ * Explicitly create a *copy* of the Binary, copying the underlying binary data too.
+ * @method copy
+ * @param {number} offset an optional offset into the underlying buffer from
+ * where we want to copy the data, default is zero.
+ * @param {number} n an optional number of bytes to copy, default is this.size - offset.
+ * @returns {proton.Data.Binary} a new {@link proton.Data.Binary} created by copying the underlying binary data.
+ * @memberof! proton.Data.Binary#
+ */
+Data['Binary'].prototype['copy'] = function(offset, n) {
+ offset = offset | 0;
+ n = n ? n : (this.size - offset);
+
+ if (offset >= this.size) {
+ offset = 0;
+ n = 0;
+ } else if ((offset + n) > this.size) {
+ n = this.size - offset; // Clamp length
+ }
+
+ var start = _malloc(n); // Allocate storage from emscripten heap.
+ _memcpy(start, this.start + offset, n); // Copy the raw data to new buffer.
+
+ return new Data['Binary'](n, start);
+};
+
+/**
+ * Converts the {@link proton.Data.Binary} to a string. This is clearly most
+ * useful when the binary data is actually a binary representation of a string
+ * such as a C style ASCII string.
+ * @method toString
+ * @memberof! proton.Data.Binary#
+ * @returns {string} the String form of a {@link proton.Data.Binary}.
+ */
+Data['Binary'].prototype.toString = Data['Binary'].prototype.valueOf = function() {
+ // Create a native JavaScript String from the start/size information.
+ return Pointer_stringify(this.start, this.size);
+};
+
diff --git a/proton-c/bindings/javascript/data-described.js b/proton-c/bindings/javascript/data-described.js
new file mode 100644
index 0000000..e1f9d84
--- /dev/null
+++ b/proton-c/bindings/javascript/data-described.js
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*****************************************************************************/
+/* */
+/* proton.Data.Described */
+/* */
+/*****************************************************************************/
+
+/**
+ * Create a proton.Data.Described.
+ * @classdesc
+ * This class represents an AMQP Described.
+ * @constructor proton.Data.Described
+ * @param {object} value the value of the described type.
+ * @param {string} descriptor an optional string describing the type.
+ * @property {object} value the actual value of the described type.
+ * @property {string} descriptor a string describing the type.
+ */
+Data['Described'] = function(value, descriptor) { // Data.Described Constructor.
+ this['value'] = value;
+ this['descriptor'] = descriptor;
+};
+
+/**
+ * @method toString
+ * @memberof! proton.Data.Described#
+ * @returns {string} the String form of a {@link proton.Data.Described}.
+ */
+Data['Described'].prototype.toString = function() {
+ return 'Described(' + this['value'] + ', ' + this['descriptor'] + ')';
+};
+
+/**
+ * @method valueOf
+ * @memberof! proton.Data.Described#
+ * @returns {object} the value of the {@link proton.Data.Described}.
+ */
+Data['Described'].prototype.valueOf = function() {
+ return this['value'];
+};
+
+/**
+ * Compare two instances of proton.Data.Described for equality.
+ * @method equals
+ * @memberof! proton.Data.Described#
+ * @param {proton.Data.Described} rhs the instance we wish to compare this instance with.
+ * @returns {boolean} true iff the two compared instances are equal.
+ */
+Data['Described'].prototype['equals'] = function(rhs) {
+ if (rhs instanceof Data['Described']) {
+ return ((this['descriptor'] === rhs['descriptor']) && (this['value'] === rhs['value']));
+ } else {
+ return false;
+ }
+};
+
diff --git a/proton-c/bindings/javascript/data-long.js b/proton-c/bindings/javascript/data-long.js
new file mode 100644
index 0000000..38fb6c8
--- /dev/null
+++ b/proton-c/bindings/javascript/data-long.js
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*****************************************************************************/
+/* */
+/* proton.Data.Long */
+/* */
+/*****************************************************************************/
+
+/**
+ * Create a proton.Data.Long.
+ * @classdesc
+ * This class represents a 64 bit Integer value. It is used primarily to pass and
+ * return 64 bit Integer values to and from the emscripten compiled proton-c library.
+ * This class is needed because JavaScript cannot natively represent 64 bit
+ * Integers with sufficient accuracy.
+ * @constructor proton.Data.Long
+ * @param {number} low the least significant word.
+ * @param {number} high the most significant word.
+ */
+// Use dot notation as it is a "protected" inner class not exported from the closure.
+Data.Long = function(low, high) { // Data.Long Constructor.
+ this.low = low | 0; // force into 32 signed bits.
+ this.high = high | 0; // force into 32 signed bits.
+};
+
+// proton.Data.Long constants.
+Data.Long.TWO_PWR_16_DBL_ = 1 << 16;
+Data.Long.TWO_PWR_32_DBL_ = Data.Long.TWO_PWR_16_DBL_ * Data.Long.TWO_PWR_16_DBL_;
+Data.Long.TWO_PWR_64_DBL_ = Data.Long.TWO_PWR_32_DBL_ * Data.Long.TWO_PWR_32_DBL_;
+Data.Long.TWO_PWR_63_DBL_ = Data.Long.TWO_PWR_64_DBL_ / 2;
+Data.Long.MAX_VALUE = new Data.Long(0xFFFFFFFF | 0, 0x7FFFFFFF | 0);
+Data.Long.MIN_VALUE = new Data.Long(0, 0x80000000 | 0);
+Data.Long.ZERO = new Data.Long(0, 0);
+Data.Long.ONE = new Data.Long(1, 0);
+
+/**
+ * @method fromNumber
+ * @memberof! proton.Data.Long#
+ * @returns {proton.Data.Long} an instance of {@link proton.Data.Long} created
+ * using a native JavaScript number.
+ */
+Data.Long.fromNumber = function(value) {
+ if (isNaN(value) || !isFinite(value)) {
+ return Data.Long.ZERO;
+ } else if (value <= -Data.Long.TWO_PWR_63_DBL_) {
+ return Data.Long.MIN_VALUE;
+ } else if (value + 1 >= Data.Long.TWO_PWR_63_DBL_) {
+ return Data.Long.MAX_VALUE;
+ } else if (value < 0) {
+ return Data.Long.fromNumber(-value).negate();
+ } else {
+ return new Data.Long(
+ (value % Data.Long.TWO_PWR_32_DBL_) | 0,
+ (value / Data.Long.TWO_PWR_32_DBL_) | 0);
+ }
+};
+
+/**
+ * Return the twos complement of this instance.
+ * @method negate
+ * @memberof! proton.Data.Long#
+ * @returns {proton.Data.Long} the twos complement of this instance.
+ */
+Data.Long.prototype.negate = function() {
+ if (this.equals(Data.Long.MIN_VALUE)) {
+ return Data.Long.MIN_VALUE;
+ } else {
+ return this.not().add(Data.Long.ONE);
+ }
+};
+
+/**
+ * Add two instances of {@link proton.Data.Long}.
+ * @method add
+ * @memberof! proton.Data.Long#
+ * @param {proton.Data.Long} rhs the instance we wish to add to this instance.
+ * @returns {proton.Data.Long} the sum of this value and the rhs.
+ */
+Data.Long.prototype.add = function(rhs) {
+ // Divide each number into 4 chunks of 16 bits, and then sum the chunks.
+
+ var a48 = this.high >>> 16;
+ var a32 = this.high & 0xFFFF;
+ var a16 = this.low >>> 16;
+ var a00 = this.low & 0xFFFF;
+
+ var b48 = rhs.high >>> 16;
+ var b32 = rhs.high & 0xFFFF;
+ var b16 = rhs.low >>> 16;
+ var b00 = rhs.low & 0xFFFF;
+
+ var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
+ c00 += a00 + b00;
+ c16 += c00 >>> 16;
+ c00 &= 0xFFFF;
+ c16 += a16 + b16;
+ c32 += c16 >>> 16;
+ c16 &= 0xFFFF;
+ c32 += a32 + b32;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c48 += a48 + b48;
+ c48 &= 0xFFFF;
+ return new Data.Long((c16 << 16) | c00, (c48 << 16) | c32);
+};
+
+/**
+ * Return the complement of this instance.
+ * @method not
+ * @memberof! proton.Data.Long#
+ * @returns {proton.Data.Long} the complement of this instance.
+ */
+Data.Long.prototype.not = function() {
+ return new Data.Long(~this.low, ~this.high);
+};
+
+/**
+ * Compare two instances of {@link proton.Data.Long} for equality.
+ * @method equals
+ * @memberof! proton.Data.Long#
+ * @param {proton.Data.Long} rhs the instance we wish to compare this instance with.
+ * @returns {boolean} true iff the two compared instances are equal.
+ */
+Data.Long.prototype.equals = function(other) {
+ return (this.high == other.high) && (this.low == other.low);
+};
+
+/**
+ * @method getHighBits
+ * @memberof! proton.Data.Long#
+ * @returns {number} the most significant word of a {@link proton.Data.Long}.
+ */
+Data.Long.prototype.getHighBits = function() {
+ return this.high;
+};
+
+/**
+ * @method getLowBits
+ * @memberof! proton.Data.Long#
+ * @returns {number} the least significant word of a {@link proton.Data.Long}.
+ */
+Data.Long.prototype.getLowBits = function() {
+ return this.low;
+};
+
+/**
+ * @method getLowBitsUnsigned
+ * @memberof! proton.Data.Long#
+ * @returns {number} the least significant word of a {@link proton.Data.Long}
+ * as an unsigned value.
+ */
+Data.Long.prototype.getLowBitsUnsigned = function() {
+ return (this.low >= 0) ? this.low : Data.Long.TWO_PWR_32_DBL_ + this.low;
+};
+
+/**
+ * @method toNumber
+ * @memberof! proton.Data.Long#
+ * @returns {number} a native JavaScript number (with possible loss of precision).
+ */
+Data.Long.prototype.toNumber = function() {
+ return (this.high * Data.Long.TWO_PWR_32_DBL_) + this.getLowBitsUnsigned();
+};
+
+/**
+ * @method toString
+ * @memberof! proton.Data.Long#
+ * @returns {string} the String form of a {@link proton.Data.Long}.
+ */
+Data.Long.prototype.toString = function() {
+ return this.high + ':' + this.getLowBitsUnsigned();
+};
+
diff --git a/proton-c/bindings/javascript/data-symbol.js b/proton-c/bindings/javascript/data-symbol.js
new file mode 100644
index 0000000..c742acb
--- /dev/null
+++ b/proton-c/bindings/javascript/data-symbol.js
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*****************************************************************************/
+/* */
+/* proton.Data.Symbol */
+/* */
+/*****************************************************************************/
+
+/**
+ * Create a proton.Data.Symbol.
+ * @classdesc
+ * This class represents an AMQP Symbol. This class is necessary primarily as a
+ * way to enable us to distinguish between a native String and a Symbol in the
+ * JavaScript type system.
+ * @constructor proton.Data.Symbol
+ * @param {string} s a symbol.
+ */
+Data['Symbol'] = function(s) { // Data.Symbol Constructor.
+ this.value = s;
+};
+
+/**
+ * @method toString
+ * @memberof! proton.Data.Symbol#
+ * @returns {string} the String form of a {@link proton.Data.Symbol}.
+ */
+Data['Symbol'].prototype.toString = Data['Symbol'].prototype.valueOf = function() {
+ return this.value;
+};
+
+/**
+ * Compare two instances of proton.Data.Symbol for equality.
+ * @method equals
+ * @memberof! proton.Data.Symbol#
+ * @param {proton.Data.Symbol} rhs the instance we wish to compare this instance with.
+ * @returns {boolean} true iff the two compared instances are equal.
+ */
+Data['Symbol'].prototype['equals'] = function(rhs) {
+ return this.toString() === rhs.toString();
+};
+
diff --git a/proton-c/bindings/javascript/data-typed-number.js b/proton-c/bindings/javascript/data-typed-number.js
new file mode 100644
index 0000000..034d4ec
--- /dev/null
+++ b/proton-c/bindings/javascript/data-typed-number.js
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*****************************************************************************/
+/* */
+/* proton.Data.TypedNumber */
+/* */
+/*****************************************************************************/
+
+// ---------------------- JavaScript Number Extensions ------------------------
+
+Number.prototype['ubyte'] = function() {
+ return new Data.TypedNumber('UBYTE', this);
+};
+
+Number.prototype['byte'] = function() {
+ return new Data.TypedNumber('BYTE', this);
+};
+
+Number.prototype['ushort'] = function() {
+ return new Data.TypedNumber('USHORT', this);
+};
+
+Number.prototype['short'] = function() {
+ return new Data.TypedNumber('SHORT', this);
+};
+
+Number.prototype['uint'] = function() {
+ return new Data.TypedNumber('UINT', this);
+};
+
+Number.prototype['int'] = function() {
+ return new Data.TypedNumber('INT', this);
+};
+
+Number.prototype['ulong'] = function() {
+ return new Data.TypedNumber('ULONG', this);
+};
+
+Number.prototype['long'] = function() {
+ return new Data.TypedNumber('LONG', this);
+};
+
+Number.prototype['float'] = function() {
+ return new Data.TypedNumber('FLOAT', this);
+};
+
+Number.prototype['double'] = function() {
+ return new Data.TypedNumber('DOUBLE', this);
+};
+
+Number.prototype['char'] = function() {
+ return new Data.TypedNumber('CHAR', this);
+};
+
+String.prototype['char'] = function() {
+ return new Data.TypedNumber('CHAR', this.charCodeAt(0));
+};
+
+// ------------------------- proton.Data.TypedNumber --------------------------
+/**
+ * Create a proton.Data.TypedNumber.
+ * @classdesc
+ * This class is a simple wrapper class that allows a "type" to be recorded for
+ * a number. The idea is that the JavaScript Number class is extended with extra
+ * methods to allow numbers to be "modified" to TypedNumbers, so for example
+ * 1.0.float() would modify 1.0 by returning a TypedNumber with type = FLOAT
+ * and value = 1. The strings used for type correspond to the names of the Data
+ * put* methods e.g. UBYTE, BYTE, USHORT, SHORT, UINT, INT, ULONG, LONG, FLOAT,
+ * DOUBLE, CHAR so that the correct method to call can be derived from the type.
+ * @constructor proton.Data.TypedNumber
+ * @param {(string|number)} type the type of the Number either as a string or number.
+ * Stored internally as a string corresponding to one of the TypeNames.
+ * @param {number} value the value of the Number.
+ */
+// Use dot notation as it is a "protected" inner class not exported from the closure.
+Data.TypedNumber = function(type, value) { // Data.TypedNumber Constructor.
+ this.type = (typeof type === 'number') ? Data['TypeNames'][type] : type;
+ this.value = value;
+};
+
+/**
+ * @method toString
+ * @memberof! proton.Data.TypedNumber#
+ * @returns {string} the String form of a {@link proton.Data.TypedNumber}.
+ */
+Data.TypedNumber.prototype.toString = Data.TypedNumber.prototype.valueOf = function() {
+ return +this.value;
+};
+
+
diff --git a/proton-c/bindings/javascript/data-uuid.js b/proton-c/bindings/javascript/data-uuid.js
new file mode 100644
index 0000000..4fee84a
--- /dev/null
+++ b/proton-c/bindings/javascript/data-uuid.js
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*****************************************************************************/
+/* */
+/* proton.Data.Uuid */
+/* */
+/*****************************************************************************/
+
+/**
+ * Create a proton.Data.Uuid which is a type 4 UUID.
+ * @classdesc
+ * This class represents a type 4 UUID, wich may use crypto libraries to generate
+ * the UUID if supported by the platform (e.g. node.js or a modern browser)
+ * @constructor proton.Data.Uuid
+ * @param {number|Array|string} u a UUID. If null a type 4 UUID is generated wich may use crypto if
+ * supported by the platform. If u is an emscripten "pointer" we copy the
+ * data from that. If u is a JavaScript Array we use it as-is. If u is a
+ * String then we try to parse that as a UUID.
+ * @property {Array} uuid is the compact array form of the UUID.
+ */
+Data['Uuid'] = function(u) { // Data.Uuid Constructor.
+ // Helper to copy from emscriptem allocated storage into JavaScript Array.
+ function _p2a(p) {
+ var uuid = new Array(16);
+ for (var i = 0; i < 16; i++) {
+ uuid[i] = getValue(p + i, 'i8') & 0xFF; // & 0xFF converts to unsigned.
+ }
+ return uuid;
+ };
+
+ if (!u) { // Generate UUID using emscriptem's uuid_generate implementation.
+ var sp = Runtime.stackSave();
+ var p = allocate(16, 'i8', ALLOC_STACK); // Create temporary pointer storage.
+ _uuid_generate(p); // Generate UUID into allocated pointer.
+ this['uuid'] = _p2a(p); // Copy from allocated storage into JavaScript Array.
+ Runtime.stackRestore(sp);
+ } else if (Data.isNumber(u)) { // Use pointer that has been passed in.
+ this['uuid'] = _p2a(u); // Copy from allocated storage into JavaScript Array.
+ } else if (Data.isArray(u)) { // Use array that has been passed in.
+ this['uuid'] = u; // Just use the JavaScript Array.
+ } else if (Data.isString(u)) { // Parse String form UUID.
+ if (u.length === 36) {
+ var i = 0;
+ var uuid = new Array(16);
+ u.toLowerCase().replace(/[0-9a-f]{2}/g, function(byte) {
+ if (i < 16) {
+ uuid[i++] = parseInt(byte, 16);
+ }
+ });
+ this['uuid'] = uuid;
+ }
+ }
+ this.string = null;
+};
+
+/**
+ * Returns the string representation of the proton.Data.Uuid.
+ * @method toString
+ * @memberof! proton.Data.Uuid#
+ * @returns {string} the String
+ * /[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}/
+ * form of a {@link proton.Data.Uuid}.
+ */
+Data['Uuid'].prototype.toString = Data['Uuid'].prototype.valueOf = function() {
+ if (!this.string) { // Check if we've cached the string version.
+ var i = 0;
+ var uu = this['uuid'];
+ var uuid = 'xxxx-xx-xx-xx-xxxxxx'.replace(/[x]/g, function(c) {
+ var r = uu[i].toString(16);
+ r = (r.length === 1) ? '0' + r : r; // Zero pad single digit hex values
+ i++;
+ return r;
+ });
+ this.string = uuid;
+ }
+ return this.string;
+};
+
+/**
+ * Compare two instances of proton.Data.Uuid for equality.
+ * @method equals
+ * @memberof! proton.Data.Uuid#
+ * @param {proton.Data.Uuid} rhs the instance we wish to compare this instance with.
+ * @returns {boolean} true iff the two compared instances are equal.
+ */
+Data['Uuid'].prototype['equals'] = function(rhs) {
+ return this.toString() === rhs.toString();
+};
+
diff --git a/proton-c/bindings/javascript/data.js b/proton-c/bindings/javascript/data.js
new file mode 100644
index 0000000..018c5fb
--- /dev/null
+++ b/proton-c/bindings/javascript/data.js
@@ -0,0 +1,1577 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*****************************************************************************/
+/* */
+/* Data */
+/* */
+/*****************************************************************************/
+
+/**
+ * Constructs a proton.Data instance.
+ * @classdesc
+ * The Data class provides an interface for decoding, extracting, creating, and
+ * encoding arbitrary AMQP data. A Data object contains a tree of AMQP values.
+ * Leaf nodes in this tree correspond to scalars in the AMQP type system such as
+ * ints<INT> or strings<STRING>. Non-leaf nodes in this tree correspond to compound
+ * values in the AMQP type system such as lists<LIST>, maps<MAP>, arrays<ARRAY>,
+ * or described values<DESCRIBED>. The root node of the tree is the Data object
+ * itself and can have an arbitrary number of children.
+ * <p>
+ * A Data object maintains the notion of the current sibling node and a current
+ * parent node. Siblings are ordered within their parent. Values are accessed
+ * and/or added by using the next, prev, enter, and exit methods to navigate to
+ * the desired location in the tree and using the supplied variety of put* and
+ * get* methods to access or add a value of the desired type.
+ * <p>
+ * The put* methods will always add a value after the current node in the tree.
+ * If the current node has a next sibling the put* method will overwrite the value
+ * on this node. If there is no current node or the current node has no next
+ * sibling then one will be added. The put* methods always set the added/modified
+ * node to the current node. The get* methods read the value of the current node
+ * and do not change which node is current.
+ * @constructor proton.Data
+ * @param {number} data an optional pointer to a pn_data_t instance. If supplied
+ * the underlying data is "owned" by another object (for example a Message)
+ * and that object is assumed to be responsible for freeing the data if
+ * necessary. If no data is supplied then the Data is stand-alone and the
+ * client application is responsible for freeing the underlying data via
+ * a call to free().
+ * @param {boolean} decodeBinaryAsString if set decode any AMQP Binary payload
+ * objects as strings. This can be useful as the data in Binary objects
+ * will be overwritten with subsequent calls to get, so they must be
+ * explicitly copied. Needless to say it is only safe to set this flag if
+ * you know that the data you are dealing with is actually a string, for
+ * example C/C++ applications often seem to encode strings as AMQP binary,
+ * a common cause of interoperability problems.
+ */
+Module['Data'] = function(data, decodeBinaryAsString) { // Data Constructor.
+ if (!data) {
+ this._data = _pn_data(16); // Default capacity is 16
+ this['free'] = function() {
+ _pn_data_free(this._data);
+ // Set free to a null function to prevent possibility of a "double free".
+ this['free'] = function() {};
+ };
+ } else {
+ this._data = data;
+ this['free'] = function() {};
+ }
+ this._decodeBinaryAsString = decodeBinaryAsString;
+};
+
+// Expose constructor as package scope variable to make internal calls less verbose.
+var Data = Module['Data'];
+
+// Expose prototype as a variable to make method declarations less verbose.
+var _Data_ = Data.prototype;
+
+// ************************** Class properties ********************************
+
+Data['NULL'] = 1;
+Data['BOOL'] = 2;
+Data['UBYTE'] = 3;
+Data['BYTE'] = 4;
+Data['USHORT'] = 5;
+Data['SHORT'] = 6;
+Data['UINT'] = 7;
+Data['INT'] = 8;
+Data['CHAR'] = 9;
+Data['ULONG'] = 10;
+Data['LONG'] = 11;
+Data['TIMESTAMP'] = 12;
+Data['FLOAT'] = 13;
+Data['DOUBLE'] = 14;
+Data['DECIMAL32'] = 15;
+Data['DECIMAL64'] = 16;
+Data['DECIMAL128'] = 17;
+Data['UUID'] = 18;
+Data['BINARY'] = 19;
+Data['STRING'] = 20;
+Data['SYMBOL'] = 21;
+Data['DESCRIBED'] = 22;
+Data['ARRAY'] = 23;
+Data['LIST'] = 24;
+Data['MAP'] = 25;
+
+/**
+ * Look-up table mapping proton-c types to the accessor method used to
+ * deserialise the type. N.B. this is a simple Array and not a map because the
+ * types that we get back from pn_data_type are integers from the pn_type_t enum.
+ * @property {Array<String>} TypeNames ['NULL', 'NULL', 'BOOL', 'UBYTE', 'BYTE',
+ * 'USHORT', 'SHORT', 'UINT', 'INT', 'CHAR', 'ULONG', 'LONG', 'TIMESTAMP',
+ * 'FLOAT', 'DOUBLE', 'DECIMAL32', 'DECIMAL64', 'DECIMAL128', 'UUID',
+ * 'BINARY', 'STRING', 'SYMBOL', 'DESCRIBED', 'ARRAY', 'LIST', 'MAP']
+ * @memberof! proton.Data
+ */
+Data['TypeNames'] = [
+ 'NULL', // 0
+ 'NULL', // PN_NULL = 1
+ 'BOOL', // PN_BOOL = 2
+ 'UBYTE', // PN_UBYTE = 3
+ 'BYTE', // PN_BYTE = 4
+ 'USHORT', // PN_USHORT = 5
+ 'SHORT', // PN_SHORT = 6
+ 'UINT', // PN_UINT = 7
+ 'INT', // PN_INT = 8
+ 'CHAR', // PN_CHAR = 9
+ 'ULONG', // PN_ULONG = 10
+ 'LONG', // PN_LONG = 11
+ 'TIMESTAMP', // PN_TIMESTAMP = 12
+ 'FLOAT', // PN_FLOAT = 13
+ 'DOUBLE', // PN_DOUBLE = 14
+ 'DECIMAL32', // PN_DECIMAL32 = 15
+ 'DECIMAL64', // PN_DECIMAL64 = 16
+ 'DECIMAL128', // PN_DECIMAL128 = 17
+ 'UUID', // PN_UUID = 18
+ 'BINARY', // PN_BINARY = 19
+ 'STRING', // PN_STRING = 20
+ 'SYMBOL', // PN_SYMBOL = 21
+ 'DESCRIBED', // PN_DESCRIBED = 22
+ 'ARRAY', // PN_ARRAY = 23
+ 'LIST', // PN_LIST = 24
+ 'MAP' // PN_MAP = 25
+];
+
+// *************************** Class methods **********************************
+
+/**
+ * Test if a given Object is a JavaScript Array.
+ * @method isArray
+ * @memberof! proton.Data
+ * @param {object} o the Object that we wish to test.
+ * @returns {boolean} true iff the Object is a JavaScript Array.
+ */
+Data.isArray = Array.isArray || function(o) {
+ return Object.prototype.toString.call(o) === '[object Array]';
+};
+
+/**
+ * Test if a given Object is a JavaScript Number.
+ * @method isNumber
+ * @memberof! proton.Data
+ * @param {object} o the Object that we wish to test.
+ * @returns {boolean} true iff the Object is a JavaScript Number.
+ */
+Data.isNumber = function(o) {
+ return typeof o === 'number' ||
+ (typeof o === 'object' && Object.prototype.toString.call(o) === '[object Number]');
+};
+
+/**
+ * Test if a given Object is a JavaScript String.
+ * @method isString
+ * @memberof! proton.Data
+ * @param {object} o the Object that we wish to test.
+ * @returns {boolean} true iff the Object is a JavaScript String.
+ */
+Data.isString = function(o) {
+ return typeof o === 'string' ||
+ (typeof o === 'object' && Object.prototype.toString.call(o) === '[object String]');
+};
+
+/**
+ * Test if a given Object is a JavaScript Boolean.
+ * @method isBoolean
+ * @memberof! proton.Data
+ * @param {object} o the Object that we wish to test.
+ * @returns {boolean} true iff the Object is a JavaScript Boolean.
+ */
+Data.isBoolean = function(o) {
+ return typeof o === 'boolean' ||
+ (typeof o === 'object' && Object.prototype.toString.call(o) === '[object Boolean]');
+};
+
+
+// ************************* Protected methods ********************************
+
+// We use the dot notation rather than associative array form for protected
+// methods so they are visible to this "package", but the Closure compiler will
+// minify and obfuscate names, effectively making a defacto "protected" method.
+
+/**
+ * This helper method checks the supplied error code, converts it into an
+ * exception and throws the exception. This method will try to use the message
+ * populated in pn_data_error(), if present, but if not it will fall
+ * back to using the basic error code rendering from pn_code().
+ * @param code the error code to check.
+ */
+_Data_._check = function(code) {
+ if (code < 0) {
+ var errno = this['getErrno']();
+ var message = errno ? this['getError']() : Pointer_stringify(_pn_code(code));
+
+ throw new Module['DataError'](message);
+ } else {
+ return code;
+ }
+};
+
+
+// *************************** Public methods *********************************
+
+/**
+ * @method getErrno
+ * @memberof! proton.Data#
+ * @returns {number} the most recent error message code.
+ */
+_Data_['getErrno'] = function() {
+ return _pn_data_errno(this._data);
+};
+
+/**
+ * @method getError
+ * @memberof! proton.Data#
+ * @returns {string} the most recent error message as a String.
+ */
+_Data_['getError'] = function() {
+ return Pointer_stringify(_pn_error_text(_pn_data_error(this._data)));
+};
+
+/**
+ * Clears the data object.
+ * @method clear
+ * @memberof! proton.Data#
+ */
+_Data_['clear'] = function() {
+ _pn_data_clear(this._data);
+};
+
+/**
+ * Clears current node and sets the parent to the root node. Clearing the current
+ * node sets it _before_ the first node, calling next() will advance to the first node.
+ * @method rewind
+ * @memberof! proton.Data#
+ */
+_Data_['rewind'] = function() {
+ _pn_data_rewind(this._data);
+};
+
+/**
+ * Advances the current node to its next sibling and returns its type. If there
+ * is no next sibling the current node remains unchanged and null is returned.
+ * @method next
+ * @memberof! proton.Data#
+ * @returns {number} the type of the next sibling or null.
+ */
+_Data_['next'] = function() {
+ var found = _pn_data_next(this._data);
+ if (found) {
+ return this.type();
+ } else {
+ return null;
+ }
+};
+
+/**
+ * Advances the current node to its previous sibling and returns its type. If there
+ * is no previous sibling the current node remains unchanged and null is returned.
+ * @method prev
+ * @memberof! proton.Data#
+ * @returns {number} the type of the previous sibling or null.
+ */
+_Data_['prev'] = function() {
+ var found = _pn_data_prev(this._data);
+ if (found) {
+ return this.type();
+ } else {
+ return null;
+ }
+};
+
+/**
+ * Sets the parent node to the current node and clears the current node. Clearing
+ * the current node sets it _before_ the first child, next() advances to the first child.
+ * @method enter
+ * @memberof! proton.Data#
+ */
+_Data_['enter'] = function() {
+ return (_pn_data_enter(this._data) > 0);
+};
+
+/**
+ * Sets the current node to the parent node and the parent node to its own parent.
+ * @method exit
+ * @memberof! proton.Data#
+ */
+_Data_['exit'] = function() {
+ return (_pn_data_exit(this._data) > 0);
+};
+
+/**
+ * Look up a value by name. N.B. Need to use getObject() to retrieve the actual
+ * value after lookup suceeds.
+ * @method lookup
+ * @memberof! proton.Data#
+ * @param {string} name the name of the property to look up.
+ * @returns {boolean} true iff the lookup succeeded.
+ */
+_Data_['lookup'] = function(name) {
+ var sp = Runtime.stackSave();
+ var lookup = _pn_data_lookup(this._data, allocate(intArrayFromString(name), 'i8', ALLOC_STACK));
+ Runtime.stackRestore(sp);
+ return (lookup > 0);
+};
+
+// TODO document - not quite sure what these are for?
+_Data_['narrow'] = function() {
+ _pn_data_narrow(this._data);
+};
+
+_Data_['widen'] = function() {
+ _pn_data_widen(this._data);
+};
+
+/**
+ * @method type
+ * @memberof! proton.Data#
+ * @returns {number} the type of the current node or null if the type is unknown.
+ */
+_Data_['type'] = function() {
+ var dtype = _pn_data_type(this._data);
+ if (dtype === -1) {
+ return null;
+ } else {
+ return dtype;
+ }
+};
+
+/**
+ * Return a Binary representation of the data encoded in AMQP format. N.B. the
+ * returned {@link proton.Data.Binary} "owns" the underlying raw data and is thus
+ * responsible for freeing it or passing it to a method that consumes a Binary
+ * such as {@link proton.Data.decode} or {@link proton.Data.putBINARY}.
+ * @method encode
+ * @memberof! proton.Data#
+ * @returns {proton.Data.Binary} a representation of the data encoded in AMQP format.
+ */
+_Data_['encode'] = function() {
+ var size = 1024;
+ while (true) {
+ var bytes = _malloc(size); // Allocate storage from emscripten heap.
+ var cd = _pn_data_encode(this._data, bytes, size);
+
+ if (cd === Module['Error']['OVERFLOW']) {
+ _free(bytes);
+ size *= 2;
+ } else if (cd >= 0) {
+ return new Data['Binary'](cd, bytes);
+ } else {
+ _free(bytes);
+ this._check(cd);
+ return;
+ }
+ }
+};
+
+/**
+ * Decodes the first value from supplied Binary AMQP data and returns a new
+ * {@link proton.Data.Binary} containing the remainder of the data or null if
+ * all the supplied data has been consumed. N.B. this method "consumes" data
+ * from a {@link proton.Data.Binary} in other words it takes responsibility for
+ * the underlying data and frees the raw data from the Binary.
+ * @method decode
+ * @memberof! proton.Data#
+ * @param {proton.Data.Binary} encoded the AMQP encoded binary data.
+ * @returns {proton.Data.Binary} a Binary containing the remaining bytes or null
+ * if all the data has been consumed.
+ */
+_Data_['decode'] = function(encoded) {
+ var start = encoded.start;
+ var size = encoded.size;
+ var consumed = this._check(_pn_data_decode(this._data, start, size));
+
+ size = size - consumed;
+ start = _malloc(size); // Allocate storage from emscripten heap.
+ _memcpy(start, encoded.start + consumed, size);
+
+ encoded['free'](); // Free the original Binary.
+ return size > 0 ? new Data['Binary'](size, start) : null;
+};
+
+/**
+ * Puts a list node. Elements may be filled by entering the list
+ * node and putting element values.
+ * <pre>
+ * var data = new proton.Data();
+ * data.putLISTNODE();
+ * data.enter();
+ * data.putINT(1);
+ * data.putINT(2);
+ * data.putINT(3);
+ * data.exit();
+ * </pre>
+ * @method putLISTNODE
+ * @memberof! proton.Data#
+ */
+_Data_['putLISTNODE'] = function() {
+ this._check(_pn_data_put_list(this._data));
+};
+
+/**
+ * Puts a map node. Elements may be filled by entering the map node
+ * and putting alternating key value pairs.
+ * <pre>
+ * var data = new proton.Data();
+ * data.putMAPNODE();
+ * data.enter();
+ * data.putSTRING('key');
+ * data.putSTRING('value');
+ * data.exit();
+ * </pre>
+ * @method putMAPNODE
+ * @memberof! proton.Data#
+ */
+_Data_['putMAPNODE'] = function() {
+ this._check(_pn_data_put_map(this._data));
+};
+
+/**
+ * Puts an array node. Elements may be filled by entering the array node and
+ * putting the element values. The values must all be of the specified array
+ * element type. If an array is described then the first child value of the array
+ * is the descriptor and may be of any type.
+ * <pre>
+ * var data = new proton.Data();
+ * data.putARRAYNODE(false, proton.Data.INT);
+ * data.enter();
+ * data.putINT(1);
+ * data.putINT(2);
+ * data.putINT(3);
+ * data.exit();
+ *
+ * data.putARRAYNODE(true, proton.Data.DOUBLE);
+ * data.enter();
+ * data.putSYMBOL('array-descriptor');
+ * data.putDOUBLE(1.1);
+ * data.putDOUBLE(1.2);
+ * data.putDOUBLE(1.3);
+ * data.exit();
+ * </pre>
+ * @method putARRAYNODE
+ * @param {boolean} described specifies whether the array is described.
+ * @param {number} type the type of the array elements.
+ * @memberof! proton.Data#
+ */
+_Data_['putARRAYNODE'] = function(described, type) {
+ this._check(_pn_data_put_array(this._data, described, type));
+};
+
+/**
+ * Puts a described node. A described node has two children, the descriptor and
+ * value. These are specified by entering the node and putting the desired values.
+ * <pre>
+ * var data = new proton.Data();
+ * data.putDESCRIBEDNODE();
+ * data.enter();
+ * data.putSYMBOL('value-descriptor');
+ * data.putSTRING('the value');
+ * data.exit();
+ * </pre>
+ * @method putDESCRIBEDNODE
+ * @memberof! proton.Data#
+ */
+_Data_['putDESCRIBEDNODE'] = function() {
+ this._check(_pn_data_put_described(this._data));
+};
+
+/**
+ * Puts a null value.
+ * @method putNULL
+ * @memberof! proton.Data#
+ */
+_Data_['putNULL'] = function() {
+ this._check(_pn_data_put_null(this._data));
+};
+
+/**
+ * Puts a boolean value.
+ * @method putBOOL
+ * @memberof! proton.Data#
+ * @param {boolean} b a boolean value.
+ */
+_Data_['putBOOL'] = function(b) {
+ this._check(_pn_data_put_bool(this._data, b));
+};
+
+/**
+ * Puts a unsigned byte value.
+ * @method putUBYTE
+ * @memberof! proton.Data#
+ * @param {number} ub an integral value.
+ */
+_Data_['putUBYTE'] = function(ub) {
+ this._check(_pn_data_put_ubyte(this._data, ub));
+};
+
+/**
+ * Puts a signed byte value.
+ * @method putBYTE
+ * @memberof! proton.Data#
+ * @param {number} b an integral value.
+ */
+_Data_['putBYTE'] = function(b) {
+ this._check(_pn_data_put_byte(this._data, b));
+};
+
+/**
+ * Puts a unsigned short value.
+ * @method putUSHORT
+ * @memberof! proton.Data#
+ * @param {number} us an integral value.
+ */
+_Data_['putUSHORT'] = function(us) {
+ this._check(_pn_data_put_ushort(this._data, us));
+};
+
+/**
+ * Puts a signed short value.
+ * @method putSHORT
+ * @memberof! proton.Data#
+ * @param {number} s an integral value.
+ */
+_Data_['putSHORT'] = function(s) {
+ this._check(_pn_data_put_short(this._data, s));
+};
+
+/**
+ * Puts a unsigned integer value.
+ * @method putUINT
+ * @memberof! proton.Data#
+ * @param {number} ui an integral value.
+ */
+_Data_['putUINT'] = function(ui) {
+ this._check(_pn_data_put_uint(this._data, ui));
+};
+
+/**
+ * Puts a signed integer value.
+ * @method putINT
+ * @memberof! proton.Data#
+ * @param {number} i an integral value.
+ */
+_Data_['putINT'] = function(i) {
+ this._check(_pn_data_put_int(this._data, i));
+};
+
+/**
+ * Puts a signed char value.
+ * @method putCHAR
+ * @memberof! proton.Data#
+ * @param {(string|number)} c a single character expressed either as a string or a number.
+ */
+_Data_['putCHAR'] = function(c) {
+ c = Data.isString(c) ? c.charCodeAt(0) : c;
+ this._check(_pn_data_put_char(this._data, c));
+};
+
+/**
+ * Puts a unsigned long value. N.B. large values can suffer from a loss of
+ * precision as JavaScript numbers are restricted to 64 bit double values.
+ * @method putULONG
+ * @memberof! proton.Data#
+ * @param {number} ul an integral value.
+ */
+_Data_['putULONG'] = function(ul) {
+ // If the supplied number exceeds the range of Data.Long invert it before
+ // constructing the Data.Long.
+ ul = (ul >= Data.Long.TWO_PWR_63_DBL_) ? (ul = -(Data.Long.TWO_PWR_64_DBL_ - ul)) : ul;
+ var long = Data.Long.fromNumber(ul);
+ this._check(_pn_data_put_ulong(this._data, long.getLowBitsUnsigned(), long.getHighBits()));
+};
+
+/**
+ * Puts a signed long value. N.B. large values can suffer from a loss of
+ * precision as JavaScript numbers are restricted to 64 bit double values.
+ * @method putLONG
+ * @memberof! proton.Data#
+ * @param {number} i an integral value.
+ */
+_Data_['putLONG'] = function(l) {
+ var long = Data.Long.fromNumber(l);
+ this._check(_pn_data_put_long(this._data, long.getLowBitsUnsigned(), long.getHighBits()));
+};
+
+/**
+ * Puts a timestamp.
+ * @method putTIMESTAMP
+ * @memberof! proton.Data#
+ * @param {(number|Date)} d a Date value.
+ */
+_Data_['putTIMESTAMP'] = function(d) {
+ // Note that a timestamp is a 64 bit number so we have to use a proton.Data.Long.
+ var timestamp = Data.Long.fromNumber(d.valueOf());
+ this._check(_pn_data_put_timestamp(this._data, timestamp.getLowBitsUnsigned(), timestamp.getHighBits()));
+};
+
+/**
+ * Puts a float value. N.B. converting between floats and doubles is imprecise
+ * so the resulting value might not quite be what you expect.
+ * @method putFLOAT
+ * @memberof! proton.Data#
+ * @param {number} f a floating point value.
+ */
+_Data_['putFLOAT'] = function(f) {
+ this._check(_pn_data_put_float(this._data, f));
+};
+
+/**
+ * Puts a double value.
+ * @method putDOUBLE
+ * @memberof! proton.Data#
+ * @param {number} d a floating point value.
+ */
+_Data_['putDOUBLE'] = function(d) {
+ this._check(_pn_data_put_double(this._data, d));
+};
+
+/**
+ * Puts a decimal32 value.
+ * @method putDECIMAL32
+ * @memberof! proton.Data#
+ * @param {number} d a decimal32 value.
+ */
+_Data_['putDECIMAL32'] = function(d) {
+ this._check(_pn_data_put_decimal32(this._data, d));
+};
+
+/**
+ * Puts a decimal64 value.
+ * @method putDECIMAL64
+ * @memberof! proton.Data#
+ * @param {number} d a decimal64 value.
+ */
+_Data_['putDECIMAL64'] = function(d) {
+ this._check(_pn_data_put_decimal64(this._data, d));
+};
+
+/**
+ * Puts a decimal128 value.
+ * @method putDECIMAL128
+ * @memberof! proton.Data#
+ * @param {number} d a decimal128 value.
+ */
+_Data_['putDECIMAL128'] = function(d) {
+ this._check(_pn_data_put_decimal128(this._data, d));
+};
+
+/**
+ * Puts a UUID value.
+ * @method putUUID
+ * @memberof! proton.Data#
+ * @param {proton.Data.Uuid} u a uuid value
+ */
+_Data_['putUUID'] = function(u) {
+ var sp = Runtime.stackSave();
+ this._check(_pn_data_put_uuid(this._data, allocate(u['uuid'], 'i8', ALLOC_STACK)));
+ Runtime.stackRestore(sp);
+};
+
+/**
+ * Puts a binary value consuming the underlying raw data in the process.
+ * @method putBINARY
+ * @memberof! proton.Data#
+ * @param {proton.Data.Binary} b a binary value.
+ */
+_Data_['putBINARY'] = function(b) {
+ var sp = Runtime.stackSave();
+ // The implementation here is a bit "quirky" due to some low-level details
+ // of the interaction between emscripten and LLVM and the use of pn_bytes.
+ // The JavaScript code below is basically a binding to:
+ //
+ // pn_data_put_binary(data, pn_bytes(b.size, b.start));
+
+ // Here's the quirky bit, pn_bytes actually returns pn_bytes_t *by value* but
+ // the low-level code handles this *by pointer* so we first need to allocate
+ // 8 bytes storage for {size, start} on the emscripten stack and then we
+ // pass the pointer to that storage as the first parameter to the pn_bytes.
+ var bytes = allocate(8, 'i8', ALLOC_STACK);
+ _pn_bytes(bytes, b.size, b.start);
+
+ // The compiled pn_data_put_binary takes the pn_bytes_t by reference not value.
+ this._check(_pn_data_put_binary(this._data, bytes));
+
+ // After calling _pn_data_put_binary the underlying Data object "owns" the
+ // binary data, so we can call free on the proton.Data.Binary instance to
+ // release any storage it has acquired back to the emscripten heap.
+ b['free']();
+ Runtime.stackRestore(sp);
+};
+
+/**
+ * Puts a unicode string value.
+ * @method putSTRING
+ * @memberof! proton.Data#
+ * @param {string} s a unicode string value.
+ */
+_Data_['putSTRING'] = function(s) {
+ var sp = Runtime.stackSave();
+ // The implementation here is a bit "quirky" due to some low-level details
+ // of the interaction between emscripten and LLVM and the use of pn_bytes.
+ // The JavaScript code below is basically a binding to:
+ //
+ // pn_data_put_string(data, pn_bytes(strlen(text), text));
+
+ // First create an array from the JavaScript String using the intArrayFromString
+ // helper function (from emscripten/src/preamble.js). We use this idiom in a
+ // few places but here we create array as a separate var as we need its length.
+ var array = intArrayFromString(s, true); // The true means don't add NULL.
+ // Allocate temporary storage for the array on the emscripten stack.
+ var str = allocate(array, 'i8', ALLOC_STACK);
+
+ // Here's the quirky bit, pn_bytes actually returns pn_bytes_t *by value* but
+ // the low-level code handles this *by pointer* so we first need to allocate
+ // 8 bytes storage for {size, start} on the emscripten stack and then we
+ // pass the pointer to that storage as the first parameter to the pn_bytes.
+ var bytes = allocate(8, 'i8', ALLOC_STACK);
+ _pn_bytes(bytes, array.length, str);
+
+ // The compiled pn_data_put_string takes the pn_bytes_t by reference not value.
+ this._check(_pn_data_put_string(this._data, bytes));
+ Runtime.stackRestore(sp);
+};
+
+/**
+ * Puts a symbolic value. According to the AMQP 1.0 Specification Symbols are
+ * values from a constrained domain. Although the set of possible domains is
+ * open-ended, typically the both number and size of symbols in use for any
+ * given application will be small, e.g. small enough that it is reasonable to
+ * cache all the distinct values. Symbols are encoded as ASCII characters.
+ * @method putSYMBOL
+ * @memberof! proton.Data#
+ * @param {proton.Data.Symbol|string} s the symbol name.
+ */
+_Data_['putSYMBOL'] = function(s) {
+ var sp = Runtime.stackSave();
+ // The implementation here is a bit "quirky" due to some low-level details
+ // of the interaction between emscripten and LLVM and the use of pn_bytes.
+ // The JavaScript code below is basically a binding to:
+ //
+ // pn_data_put_symbol(data, pn_bytes(strlen(text), text));
+
+ // First create an array from the JavaScript String using the intArrayFromString
+ // helper function (from emscripten/src/preamble.js). We use this idiom in a
+ // few places but here we create array as a separate var as we need its length.
+ var array = intArrayFromString(s, true); // The true means don't add NULL.
+ // Allocate temporary storage for the array on the emscripten stack.
+ var str = allocate(array, 'i8', ALLOC_STACK);
+
+ // Here's the quirky bit, pn_bytes actually returns pn_bytes_t *by value* but
+ // the low-level code handles this *by pointer* so we first need to allocate
+ // 8 bytes storage for {size, start} on the emscripten stack and then we
+ // pass the pointer to that storage as the first parameter to the pn_bytes.
+ var bytes = allocate(8, 'i8', ALLOC_STACK);
+ _pn_bytes(bytes, array.length, str);
+
+ // The compiled pn_data_put_symbol takes the pn_bytes_t by reference not value.
+ this._check(_pn_data_put_symbol(this._data, bytes));
+ Runtime.stackRestore(sp);
+};
+
+/**
+ * If the current node is a list node, return the number of elements,
+ * otherwise return zero. List elements can be accessed by entering
+ * the list.
+ * <pre>
+ * var count = data.getLISTNODE();
+ * data.enter();
+ * for (var i = 0; i < count; i++) {
+ * var type = data.next();
+ * if (type === proton.Data.STRING) {
+ * console.log(data.getSTRING());
+ * }
+ * }
+ * data.exit();
+ * </pre>
+ * @method getLISTNODE
+ * @memberof! proton.Data#
+ * @returns {number} the number of elements if the current node is a list,
+ * zero otherwise.
+ */
+_Data_['getLISTNODE'] = function() {
+ return _pn_data_get_list(this._data);
+};
+
+/**
+ * If the current node is a map, return the number of child elements,
+ * otherwise return zero. Key value pairs can be accessed by entering
+ * the map.
+ * <pre>
+ * var count = data.getMAPNODE();
+ * data.enter();
+ * for (var i = 0; i < count/2; i++) {
+ * var type = data.next();
+ * if (type === proton.Data.STRING) {
+ * console.log(data.getSTRING());
+ * }
+ * }
+ * data.exit();
+ * </pre>
+ * @method getMAPNODE
+ * @memberof! proton.Data#
+ * @returns {number} the number of elements if the current node is a list,
+ * zero otherwise.
+ */
+_Data_['getMAPNODE'] = function() {
+ return _pn_data_get_map(this._data);
+};
+
+/**
+ * If the current node is an array, return an object containing the tuple of the
+ * element count, a boolean indicating whether the array is described, and the
+ * type of each element, otherwise return {count: 0, described: false, type: null).
+ * Array data can be accessed by entering the array.
+ * <pre>
+ * // Read an array of strings with a symbolic descriptor
+ * var metadata = data.getARRAYNODE();
+ * var count = metadata.count;
+ * data.enter();
+ * data.next();
+ * console.log("Descriptor:" + data.getSYMBOL());
+ * for (var i = 0; i < count; i++) {
+ * var type = data.next();
+ * console.log("Element:" + data.getSTRING());
+ * }
+ * data.exit();
+ * </pre>
+ * @method getARRAYNODE
+ * @memberof! proton.Data#
+ * @returns {object} the tuple of the element count, a boolean indicating whether
+ * the array is described, and the type of each element.
+ */
+_Data_['getARRAYNODE'] = function() {
+ var count = _pn_data_get_array(this._data);
+ var described = (_pn_data_is_array_described(this._data) > 0);
+ var type = _pn_data_get_array_type(this._data);
+ type = (type == -1) ? null : type;
+ return {'count': count, 'described': described, 'type': type};
+};
+
+/**
+ * Checks if the current node is a described node. The descriptor and value may
+ * be accessed by entering the described node.
+ * <pre>
+ * // read a symbolically described string
+ * assert(data.isDESCRIBEDNODE()); // will error if the current node is not described
+ * data.enter();
+ * console.log(data.getSYMBOL());
+ * console.log(data.getSTRING());
+ * data.exit();
+ * </pre>
+ * @method isDESCRIBEDNODE
+ * @memberof! proton.Data#
+ * @returns {boolean} true iff the current node is a described, false otherwise.
+ */
+_Data_['isDESCRIBEDNODE'] = function() {
+ return _pn_data_is_described(this._data);
+};
+
+/**
+ * @method getNULL
+ * @memberof! proton.Data#
+ * @returns a null value.
+ */
+_Data_['getNULL'] = function() {
+ return null;
+};
+
+/**
+ * Checks if the current node is a null.
+ * @method isNULL
+ * @memberof! proton.Data#
+ * @returns {boolean} true iff the current node is null.
+ */
+_Data_['isNULL'] = function() {
+ return (_pn_data_is_null(this._data) > 0);
+};
+
+/**
+ * @method getBOOL
+ * @memberof! proton.Data#
+ * @returns {boolean} a boolean value if the current node is a boolean, returns
+ * false otherwise.
+ */
+_Data_['getBOOL'] = function() {
+ return (_pn_data_get_bool(this._data) > 0);
+};
+
+/**
+ * @method getUBYTE
+ * @memberof! proton.Data#
+ * @returns {number} value if the current node is an unsigned byte, returns 0 otherwise.
+ */
+_Data_['getUBYTE'] = function() {
+ return _pn_data_get_ubyte(this._data) & 0xFF; // & 0xFF converts to unsigned;
+};
+
+/**
+ * @method getBYTE
+ * @memberof! proton.Data#
+ * @returns {number} value if the current node is a signed byte, returns 0 otherwise.
+ */
+_Data_['getBYTE'] = function() {
+ return _pn_data_get_byte(this._data);
+};
+
+/**
+ * @method getUSHORT
+ * @memberof! proton.Data#
+ * @returns {number} value if the current node is an unsigned short, returns 0 otherwise.
+ */
+_Data_['getUSHORT'] = function() {
+ return _pn_data_get_ushort(this._data) & 0xFFFF; // & 0xFFFF converts to unsigned;
+};
+
+/**
+ * @method getSHORT
+ * @memberof! proton.Data#
+ * @returns {number} value if the current node is a signed short, returns 0 otherwise.
+ */
+_Data_['getSHORT'] = function() {
+ return _pn_data_get_short(this._data);
+};
+
+/**
+ * @method getUINT
+ * @memberof! proton.Data#
+ * @returns {number} value if the current node is an unsigned int, returns 0 otherwise.
+ */
+_Data_['getUINT'] = function() {
+ var value = _pn_data_get_uint(this._data);
+ return (value > 0) ? value : 4294967296 + value; // 4294967296 == 2^32
+};
+
+/**
+ * @method getINT
+ * @memberof! proton.Data#
+ * @returns {number} value if the current node is a signed int, returns 0 otherwise.
+ */
+_Data_['getINT'] = function() {
+ return _pn_data_get_int(this._data);
+};
+
+/**
+ * @method getCHAR
+ * @memberof! proton.Data#
+ * @returns {string} the character represented by the unicode value of the current node.
+ */
+_Data_['getCHAR'] = function() {
+ return String.fromCharCode(_pn_data_get_char(this._data));
+};
+
+/**
+ * Retrieve an unsigned long value. N.B. large values can suffer from a loss of
+ * precision as JavaScript numbers are restricted to 64 bit double values.
+ * @method getULONG
+ * @memberof! proton.Data#
+ * @returns {proton.Data.Long} value if the current node is an unsigned long, returns 0 otherwise.
+ */
+_Data_['getULONG'] = function() {
+ var low = _pn_data_get_ulong(this._data);
+ var high = Runtime.getTempRet0();
+ var long = new Data.Long(low, high);
+ long = long.toNumber();
+ return (long >= 0) ? long : Data.Long.TWO_PWR_64_DBL_ + long;
+};
+
+/**
+ * Retrieve a signed long value. N.B. large values can suffer from a loss of
+ * precision as JavaScript numbers are restricted to 64 bit double values.
+ * @method getLONG
+ * @memberof! proton.Data#
+ * @returns {proton.Data.Long} value if the current node is a signed long, returns 0 otherwise.
+ */
+_Data_['getLONG'] = function() {
+ // Getting the long is a little tricky as it is a 64 bit number. The way
+ // emscripten handles this is to return the low 32 bits directly and pass
+ // the high 32 bits via the tempRet0 variable. We use Data.Long to hold
+ // the 64 bit number and Data.Long.toNumber() to convert it back into a
+ // JavaScript number.
+ var low = _pn_data_get_long(this._data);
+ var high = Runtime.getTempRet0();
+ var long = new Data.Long(low, high);
+ long = long.toNumber();
+ return long;
+};
+
+/**
+ * @method getTIMESTAMP
+ * @memberof! proton.Data#
+ * @returns {Date} a native JavaScript Date instance representing the timestamp.
+ */
+_Data_['getTIMESTAMP'] = function() {
+ // Getting the timestamp is a little tricky as it is a 64 bit number. The way
+ // emscripten handles this is to return the low 32 bits directly and pass
+ // the high 32 bits via the tempRet0 variable. We use Data.Long to hold
+ // the 64 bit number and Data.Long.toNumber() to convert it back into a
+ // JavaScript number.
+ var low = _pn_data_get_timestamp(this._data);
+ var high = Runtime.getTempRet0();
+ var long = new Data.Long(low, high);
+ long = long.toNumber();
+ return new Date(long);
+};
+
+/**
+ * Retrieves a float value. N.B. converting between floats and doubles is imprecise
+ * so the resulting value might not quite be what you expect.
+ * @method getFLOAT
+ * @memberof! proton.Data#
+ * @returns {number} value if the current node is a float, returns 0 otherwise.
+ */
+_Data_['getFLOAT'] = function() {
+ return _pn_data_get_float(this._data);
+};
+
+/**
+ * @method getDOUBLE
+ * @memberof! proton.Data#
+ * @returns {number} value if the current node is a double, returns 0 otherwise.
+ */
+_Data_['getDOUBLE'] = function() {
+ return _pn_data_get_double(this._data);
+};
+
+/**
+ * @method getDECIMAL32
+ * @memberof! proton.Data#
+ * @returns {number} value if the current node is a decimal32, returns 0 otherwise.
+ */
+_Data_['getDECIMAL32'] = function() {
+console.log("getDECIMAL32 not properly implemented yet");
+ return _pn_data_get_decimal32(this._data);
+};
+
+/**
+ * @method getDECIMAL64
+ * @memberof! proton.Data#
+ * @returns {number} value if the current node is a decimal64, returns 0 otherwise.
+ */
+_Data_['getDECIMAL64'] = function() {
+console.log("getDECIMAL64 not properly implemented yet");
+ return _pn_data_get_decimal64(this._data);
+};
+
+/**
+ * @method getDECIMAL128
+ * @memberof! proton.Data#
+ * @returns {number} value if the current node is a decimal128, returns 0 otherwise.
+ */
+_Data_['getDECIMAL128'] = function() {
+console.log("getDECIMAL128 not properly implemented yet");
+ return _pn_data_get_decimal128(this._data);
+};
+
+/**
+ * @method getUUID
+ * @memberof! proton.Data#
+ * @returns {proton.Data.Uuid} value if the current node is a UUID, returns null otherwise.
+ */
+_Data_['getUUID'] = function() {
+ var sp = Runtime.stackSave();
+
+ // Here's the quirky bit, pn_data_get_uuid actually returns pn_uuid_t
+ // *by value* but the low-level code handles this *by pointer* so we first
+ // need to allocate 16 bytes storage for pn_uuid_t on the emscripten stack
+ // and then we pass the pointer to that storage as the first parameter to the
+ // compiled pn_data_get_uuid.
+ var bytes = allocate(16, 'i8', ALLOC_STACK); // pn_uuid_t is 16 bytes.
+ _pn_data_get_uuid(bytes, this._data);
+
+ // Create a new UUID from the bytes
+ var uuid = new Data['Uuid'](bytes);
+
+ // Tidy up the memory that we allocated on emscripten's stack.
+ Runtime.stackRestore(sp);
+
+ return uuid;
+};
+
+/**
+ * @method getBINARY
+ * @memberof! proton.Data#
+ * @returns {proton.Data.Binary} value if the current node is a Binary, returns null otherwise.
+ */
+_Data_['getBINARY'] = function() {
+ var sp = Runtime.stackSave();
+ // The implementation here is a bit "quirky" due to some low-level details
+ // of the interaction between emscripten and LLVM and the use of pn_bytes.
+ // The JavaScript code below is basically a binding to:
+ //
+ // pn_bytes bytes = pn_data_get_binary(data);
+
+ // Here's the quirky bit, pn_data_get_binary actually returns pn_bytes_t
+ // *by value* but the low-level code handles this *by pointer* so we first
+ // need to allocate 8 bytes storage for {size, start} on the emscripten stack
+ // and then we pass the pointer to that storage as the first parameter to the
+ // compiled pn_data_get_binary.
+ var bytes = allocate(8, 'i8', ALLOC_STACK);
+ _pn_data_get_binary(bytes, this._data);
+
+ // The bytes variable is really of type pn_bytes_t* so we use emscripten's
+ // getValue() call to retrieve the size and then the start pointer.
+ var size = getValue(bytes, 'i32');
+ var start = getValue(bytes + 4, '*');
+
+ // Create a proton.Data.Binary from the pn_bytes_t information.
+ var binary = new Data['Binary'](size, start);
+
+ // Tidy up the memory that we allocated on emscripten's stack.
+ Runtime.stackRestore(sp);
+
+ // If _decodeBinaryAsString is set return the stringified form of the Binary.
+ if (this._decodeBinaryAsString) {
+ return binary.toString();
+ } else {
+ return binary;
+ }
+};
+
+/**
+ * Gets a unicode String value from the current node.
+ * @method getSTRING
+ * @memberof! proton.Data#
+ * @returns {string} value if the current node is a String, returns "" otherwise.
+ */
+_Data_['getSTRING'] = function() {
+ var sp = Runtime.stackSave();
+ // The implementation here is a bit "quirky" due to some low-level details
+ // of the interaction between emscripten and LLVM and the use of pn_bytes.
+ // The JavaScript code below is basically a binding to:
+ //
+ // pn_bytes bytes = pn_data_get_string(data);
+
+ // Here's the quirky bit, pn_data_get_string actually returns pn_bytes_t
+ // *by value* but the low-level code handles this *by pointer* so we first
+ // need to allocate 8 bytes storage for {size, start} on the emscripten stack
+ // and then we pass the pointer to that storage as the first parameter to the
+ // compiled pn_data_get_string.
+ var bytes = allocate(8, 'i8', ALLOC_STACK);
+ _pn_data_get_string(bytes, this._data);
+
+ // The bytes variable is really of type pn_bytes_t* so we use emscripten's
+ // getValue() call to retrieve the size and then the start pointer.
+ var size = getValue(bytes, 'i32');
+ var start = getValue(bytes + 4, '*');
+
+ // Create a native JavaScript String from the pn_bytes_t information.
+ var string = Pointer_stringify(start, size);
+
+ // Tidy up the memory that we allocated on emscripten's stack.
+ Runtime.stackRestore(sp);
+
+ return string;
+};
+
+/**
+ * Gets a symbolic value. According to the AMQP 1.0 Specification Symbols are
+ * values from a constrained domain. Although the set of possible domains is
+ * open-ended, typically the both number and size of symbols in use for any
+ * given application will be small, e.g. small enough that it is reasonable to
+ * cache all the distinct values. Symbols are encoded as ASCII characters.
+ * @method getSYMBOL
+ * @memberof! proton.Data#
+ * @returns {proton.Data.Symbol} value if the current node is a Symbol, returns "" otherwise.
+ */
+_Data_['getSYMBOL'] = function() {
+ var sp = Runtime.stackSave();
+ // The implementation here is a bit "quirky" due to some low-level details
+ // of the interaction between emscripten and LLVM and the use of pn_bytes.
+ // The JavaScript code below is basically a binding to:
+ //
+ // pn_bytes bytes = pn_data_get_symbol(data);
+
+ // Here's the quirky bit, pn_data_get_symbol actually returns pn_bytes_t
+ // *by value* but the low-level code handles this *by pointer* so we first
+ // need to allocate 8 bytes storage for {size, start} on the emscripten stack
+ // and then we pass the pointer to that storage as the first parameter to the
+ // compiled pn_data_get_symbol.
+ var bytes = allocate(8, 'i8', ALLOC_STACK);
+ _pn_data_get_symbol(bytes, this._data);
+
+ // The bytes variable is really of type pn_bytes_t* so we use emscripten's
+ // getValue() call to retrieve the size and then the start pointer.
+ var size = getValue(bytes, 'i32');
+ var start = getValue(bytes + 4, '*');
+
+ // Create a native JavaScript String from the pn_bytes_t information.
+ var string = Pointer_stringify(start, size);
+
+ // Tidy up the memory that we allocated on emscripten's stack.
+ Runtime.stackRestore(sp);
+
+ return new Data['Symbol'](string);
+};
+
+/**
+ * Performs a deep copy of the current {@link proton.Data} instance and returns it
+ * @method copy
+ * @memberof! proton.Data#
+ * @returns {proton.Data} a copy of the current {@link proton.Data} instance.
+ */
+_Data_['copy'] = function() {
+ var copy = new Data();
+ this._check(_pn_data_copy(copy._data, this._data));
+ return copy;
+};
+
+/**
+ * Format the encoded AMQP Data into a string representation and return it.
+ * @method format
+ * @memberof! proton.Data#
+ * @returns {string} a formatted string representation of the encoded Data.
+ */
+_Data_['format'] = function() {
+ var size = 1024; // Pass by reference variable - need to use setValue to initialise it.
+ while (true) {
+ setValue(size, size, 'i32'); // Set pass by reference variable.
+ var bytes = _malloc(size); // Allocate storage from emscripten heap.
+ var err = _pn_data_format(this._data, bytes, size);
+ var size = getValue(size, 'i32'); // Dereference the real size value;
+
+ if (err === Module['Error']['OVERFLOW']) {
+ _free(bytes);
+ size *= 2;
+ } else {
+ var string = Pointer_stringify(bytes);
+ _free(bytes);
+ this._check(err)
+ return string;
+ }
+ }
+};
+
+/**
+ * Print the internal state of the {@link proton.Data} in human readable form.
+ * TODO. This seems to "crash" if compound nodes such as DESCRIBED, MAP or LIST
+ * are present in the tree, this is most likely a problem with the underlying C
+ * implementation as all the other navigation and format methods work - need to
+ * check by testing with some native C code.
+ * @method dump
+ * @memberof! proton.Data#
+ */
+_Data_['dump'] = function() {
+ _pn_data_dump(this._data);
+};
+
+/**
+ * Serialise a Native JavaScript Object into an AMQP Map.
+ * @method putMAP
+ * @memberof! proton.Data#
+ * @param {object} object the Native JavaScript Object that we wish to serialise.
+ */
+_Data_['putMAP'] = function(object) {
+ this['putMAPNODE']();
+ this['enter']();
+ for (var key in object) {
+ if (object.hasOwnProperty(key)) {
+ this['putObject'](key);
+ this['putObject'](object[key]);
+ }
+ }
+ this['exit']();
+};
+
+/**
+ * Deserialise from an AMQP Map into a Native JavaScript Object.
+ * @method getMAP
+ * @memberof! proton.Data#
+ * @returns {object} the deserialised Native JavaScript Object.
+ */
+_Data_['getMAP'] = function() {
+ if (this['enter']()) {
+ var result = {};
+ while (this['next']()) {
+ var key = this['getObject']();
+ var value = null;
+ if (this['next']()) {
+ value = this['getObject']();
+ }
+ result[key] = value;
+ }
+ this['exit']();
+ return result;
+ }
+};
+
+/**
+ * Serialise a Native JavaScript Array into an AMQP List.
+ * @method putLIST
+ * @memberof! proton.Data#
+ * @param {Array} array the Native JavaScript Array that we wish to serialise.
+ */
+_Data_['putLIST'] = function(array) {
+ this['putLISTNODE']();
+ this['enter']();
+ for (var i = 0, len = array.length; i < len; i++) {
+ this['putObject'](array[i]);
+ }
+ this['exit']();
+};
+
+/**
+ * Deserialise from an AMQP List into a Native JavaScript Array.
+ * @method getLIST
+ * @memberof! proton.Data#
+ * @returns {Array} the deserialised Native JavaScript Array.
+ */
+_Data_['getLIST'] = function() {
+ if (this['enter']()) {
+ var result = [];
+ while (this['next']()) {
+ result.push(this['getObject']());
+ }
+ this['exit']();
+ return result;
+ }
+};
+
+/**
+ * Serialise a proton.Data.Described into an AMQP Described.
+ * @method putDESCRIBED
+ * @memberof! proton.Data#
+ * @param {proton.Data.Described} d the proton.Data.Described that we wish to serialise.
+ */
+_Data_['putDESCRIBED'] = function(d) {
+ this['putDESCRIBEDNODE']();
+ this['enter']();
+ this['putObject'](d['descriptor']);
+ this['putObject'](d['value']);
+ this['exit']();
+};
+
+/**
+ * Deserialise from an AMQP Described into a proton.Data.Described.
+ * @method getDESCRIBED
+ * @memberof! proton.Data#
+ * @returns {proton.Data.Described} the deserialised proton.Data.Described.
+ */
+_Data_['getDESCRIBED'] = function() {
+ if (this['enter']()) {
+ this['next']();
+ var descriptor = this['getObject']();
+ this['next']();
+ var value = this['getObject']();
+ this['exit']();
+ return new Data['Described'](value, descriptor);
+ }
+};
+
+/**
+ * Serialise a proton.Data.Array or JavaScript TypedArray into an AMQP Array.
+ * @method putARRAY
+ * @memberof! proton.Data#
+ * @param {object} a the proton.Data.Array or TypedArray that we wish to serialise.
+ */
+_Data_['putARRAY'] = function(a) {
+ var type = 1;
+ var descriptor = 'TypedArray';
+ var array = a;
+
+ if (a instanceof Data['Array']) { // Array is a proton.Data.Array
+ type = Data[a['type']]; // Find the integer type from its name string.
+ descriptor = a['descriptor'];
+ array = a['elements'];
+ } else { // Array is a Native JavaScript TypedArray so work out the right type.
+ if (a instanceof Int8Array) {
+ type = Data['BYTE'];
+ } else if (a instanceof Uint8Array || a instanceof Uint8ClampedArray) {
+ type = Data['UBYTE'];
+ } else if (a instanceof Int16Array) {
+ type = Data['SHORT'];
+ } else if (a instanceof Uint16Array) {
+ type = Data['USHORT'];
+ } else if (a instanceof Int32Array) {
+ type = Data['INT'];
+ } else if (a instanceof Uint32Array) {
+ type = Data['UINT'];
+ } else if (a instanceof Float32Array) {
+ type = Data['FLOAT'];
+ } else if (a instanceof Float64Array) {
+ type = Data['DOUBLE'];
+ }
+ }
+
+ var described = descriptor != null;
+
+ this['putARRAYNODE'](described, type);
+ this['enter']();
+ if (described) {
+ this['putObject'](descriptor);
+ }
+ var putter = 'put' + Data['TypeNames'][type];
+ for (var i = 0, len = array.length; i < len; i++) {
+ var value = array[i];
+ value = (value instanceof Data.TypedNumber) ? value.value : value;
+ this[putter](value);
+ }
+ this['exit']();
+};
+
+/**
+ * Deserialise from an AMQP Array into a proton.Data.Array.
+ * @method getARRAY
+ * @memberof! proton.Data#
+ * @returns {proton.Data.Array} the deserialised proton.Data.Array.
+ */
+_Data_['getARRAY'] = function() {
+ var metadata = this['getARRAYNODE']();
+ var count = metadata['count'];
+ var described = metadata['described'];
+ var type = metadata['type'];
+
+ if (type === null) {
+ return null;
+ }
+
+ var elements = null;
+ if (typeof ArrayBuffer === 'function') {
+ if (type === Data['BYTE']) {
+ elements = new Int8Array(count);
+ } else if (type === Data['UBYTE']) {
+ elements = new Uint8Array(count);
+ } else if (type === Data['SHORT']) {
+ elements = new Int16Array(count);
+ } else if (type === Data['USHORT']) {
+ elements = new Uint16Array(count);
+ } else if (type === Data['INT']) {
+ elements = new Int32Array(count);
+ } else if (type === Data['UINT']) {
+ elements = new Uint32Array(count);
+ } else if (type === Data['FLOAT']) {
+ elements = new Float32Array(count);
+ } else if (type === Data['DOUBLE']) {
+ elements = new Float64Array(count);
+ } else {
+ elements = new Array(count);
+ }
+ } else {
+ elements = new Array(count);
+ }
+
+ if (this['enter']()) {
+ var descriptor; // Deliberately initialised as undefined not null.
+ if (described) {
+ this['next']();
+ descriptor = this['getObject']();
+ }
+
+ for (var i = 0; i < count; i++) {
+ this['next']();
+ elements[i] = this['getObject']();
+ }
+
+ this['exit']();
+ if (descriptor === 'TypedArray') {
+ return elements;
+ } else {
+ return new Data['Array'](type, elements, descriptor);
+ }
+ }
+};
+
+/**
+ * This method is the entry point for serialising native JavaScript types into
+ * AMQP types. In an ideal world there would be a nice clean one to one mapping
+ * and we could employ a look-up table but in practice the JavaScript type system
+ * doesn't really lend itself to that and we have to employ extra checks,
+ * heuristics and inferences.
+ * @method putObject
+ * @memberof! proton.Data#
+ * @param {object} obj the JavaScript Object or primitive to be serialised.
+ */
+_Data_['putObject'] = function(obj) {
+//console.log("Data.putObject " + obj);
+
+ if (obj == null) { // == Checks for null and undefined.
+ this['putNULL']();
+ } else if (Data.isString(obj)) {
+ var quoted = obj.match(/(['"])[^'"]*\1/);
+ if (quoted) { // If a quoted string extract the string inside the quotes.
+ obj = quoted[0].slice(1, -1);
+ }
+ this['putSTRING'](obj);
+ } else if (obj instanceof Date) {
+ this['putTIMESTAMP'](obj);
+ } else if (obj instanceof Data['Uuid']) {
+ this['putUUID'](obj);
+ } else if (obj instanceof Data['Binary']) {
+ this['putBINARY'](obj);
+ } else if (obj instanceof Data['Symbol']) {
+ this['putSYMBOL'](obj);
+ } else if (obj instanceof Data['Described']) {
+ this['putDESCRIBED'](obj);
+ } else if (obj instanceof Data['Array']) {
+ this['putARRAY'](obj);
+ } else if (obj.buffer && (typeof ArrayBuffer === 'function') &&
+ obj.buffer instanceof ArrayBuffer) {
+ this['putARRAY'](obj);
+ } else if (obj instanceof Data.TypedNumber) { // Dot notation used for "protected" inner class.
+ // Call the appropriate serialisation method based upon the numerical type.
+ this['put' + obj.type](obj.value);
+ } else if (Data.isNumber(obj)) {
+ /**
+ * This block encodes standard JavaScript numbers by making some inferences.
+ * Encoding JavaScript numbers is surprisingly complex and has several
+ * gotchas. The code here tries to do what the author believes is the
+ * most "intuitive" encoding of the native JavaScript Number. It first
+ * tries to identify if the number is an integer or floating point type
+ * by checking if the number modulo 1 is zero (i.e. if it has a remainder
+ * then it's a floating point type, which is encoded here as a double).
+ * If the number is an integer type a test is made to check if it is a
+ * 32 bit Int value. N.B. gotcha - JavaScript automagically coerces floating
+ * point numbers with a zero Fractional Part into an *exact* integer so
+ * numbers like 1.0, 100.0 etc. will be encoded as int or long here,
+ * which is unlikely to be what is wanted. There's no easy "transparent"
+ * way around this. The TypedNumber approach above allows applications
+ * to express more explicitly what is required, for example (1.0).float()
+ * (1).ubyte(), (5).long() etc.
+ */
+ if (obj % 1 === 0) {
+ if (obj === (obj|0)) { // the |0 coerces to a 32 bit value.
+ // 32 bit integer - encode as an INT.
+ this['putINT'](obj);
+ } else { // Longer than 32 bit - encode as a Long.
+ this['putLONG'](obj);
+ }
+ } else { // Floating point type - encode as a Double
+ this['putDOUBLE'](obj);
+ }
+ } else if (Data.isBoolean(obj)) {
+ this['putBOOL'](obj);
+ } else if (Data.isArray(obj)) { // Native JavaScript Array
+ this['putLIST'](obj);
+ } else {
+ this['putMAP'](obj);
+ }
+};
+
+/**
+ * @method getObject
+ * @memberof! proton.Data#
+ * @returns {object} the JavaScript Object or primitive being deserialised.
+ */
+_Data_['getObject'] = function() {
+ var type = Data['TypeNames'][this.type()];
+ type = type ? type : 'NULL';
+ var getter = 'get' + type;
+ return this[getter]();
+};
+
diff --git a/proton-c/bindings/javascript/error.js b/proton-c/bindings/javascript/error.js
new file mode 100644
index 0000000..4069bef
--- /dev/null
+++ b/proton-c/bindings/javascript/error.js
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*****************************************************************************/
+/* */
+/* Status */
+/* */
+/*****************************************************************************/
+
+/**
+ * Export Status Enum, avoiding minification.
+ * @enum
+ * @alias Status
+ * @memberof proton
+ */
+Module['Status'] = {
+ /** PN_STATUS_UNKNOWN */ 'UNKNOWN': 0, // The tracker is unknown.
+ /** PN_STATUS_PENDING */ 'PENDING': 1, // The message is in flight.
+ // For outgoing messages, use messenger.isBuffered()
+ // to see if it has been sent or not.
+ /** PN_STATUS_ACCEPTED */ 'ACCEPTED': 2, // The message was accepted.
+ /** PN_STATUS_REJECTED */ 'REJECTED': 3, // The message was rejected.
+ /** PN_STATUS_RELEASED */ 'RELEASED': 4, // The message was released.
+ /** PN_STATUS_MODIFIED */ 'MODIFIED': 5, // The message was modified.
+ /** PN_STATUS_ABORTED */ 'ABORTED': 6, // The message was aborted.
+ /** PN_STATUS_SETTLED */ 'SETTLED': 7 // The remote party has settled the message.
+};
+
+
+/*****************************************************************************/
+/* */
+/* Error */
+/* */
+/*****************************************************************************/
+
+/**
+ * Export Error Enum, avoiding minification.
+ * @enum
+ * @alias Error
+ * @memberof proton
+ */
+Module['Error'] = {
+ /** PN_EOS */ 'EOS': -1,
+ /** PN_ERR */ 'ERR': -2,
+ /** PN_OVERFLOW */ 'OVERFLOW': -3,
+ /** PN_UNDERFLOW */ 'UNDERFLOW': -4,
+ /** PN_STATE_ERR */ 'STATE_ERR': -5,
+ /** PN_ARG_ERR */ 'ARG_ERR': -6,
+ /** PN_TIMEOUT */ 'TIMEOUT': -7,
+ /** PN_INTR */ 'INTR': -8,
+ /** PN_INPROGRESS */ 'INPROGRESS': -9
+};
+
+
+/*****************************************************************************/
+/* */
+/* MessengerError */
+/* */
+/*****************************************************************************/
+
+/**
+ * Constructs a proton.MessengerError instance.
+ * @classdesc This class is a subclass of Error.
+ * @constructor proton.MessengerError
+ * @param {string} message the error message.
+ */
+Module['MessengerError'] = function(message) { // MessengerError constructor.
+ this.name = "MessengerError";
+ this.message = (message || "");
+};
+
+Module['MessengerError'].prototype = new Error();
+Module['MessengerError'].prototype.constructor = Module['MessengerError'];
+
+Module['MessengerError'].prototype.toString = function() {
+ return this.name + ': ' + this.message;
+};
+
+
+/*****************************************************************************/
+/* */
+/* SubscriptionError */
+/* */
+/*****************************************************************************/
+
+/**
+ * Constructs a proton.SubscriptionError instance.
+ * @classdesc This class is a subclass of MessengerError.
+ * @constructor proton.SubscriptionError
+ * @param {string} source the address that we want to subscribe to.
+ * @param {string} message the error message.
+ */
+Module['SubscriptionError'] = function(source, message) { // SubscriptionError constructor.
+ this.name = "SubscriptionError";
+ this.source = source;
+ this.message = (message || "");
+};
+
+Module['SubscriptionError'].prototype = new Module['MessengerError']();
+Module['SubscriptionError'].prototype.constructor = Module['SubscriptionError'];
+
+Module['SubscriptionError'].prototype.toString = function() {
+ return this.name + ': ' + this.source + ': ' + this.message;
+};
+
+
+/*****************************************************************************/
+/* */
+/* MessageError */
+/* */
+/*****************************************************************************/
+
+/**
+ * Constructs a proton.MessageError instance.
+ * @classdesc This class is a subclass of Error.
+ * @constructor proton.MessageError
+ * @param {string} message the error message.
+ */
+Module['MessageError'] = function(message) { // MessageError constructor.
+ this.name = "MessageError";
+ this.message = (message || "");
+};
+
+Module['MessageError'].prototype = new Error();
+Module['MessageError'].prototype.constructor = Module['MessageError'];
+
+Module['MessageError'].prototype.toString = function() {
+ return this.name + ': ' + this.message;
+};
+
+
+/*****************************************************************************/
+/* */
+/* DataError */
+/* */
+/*****************************************************************************/
+
+/**
+ * Constructs a proton.DataError instance.
+ * @classdesc This class is a subclass of Error.
+ * @constructor proton.DataError
+ * @param {string} message the error message.
+ */
+Module['DataError'] = function(message) { // DataError constructor.
+ this.name = "DataError";
+ this.message = (message || "");
+};
+
+Module['DataError'].prototype = new Error();
+Module['DataError'].prototype.constructor = Module['DataError'];
+
+Module['DataError'].prototype.toString = function() {
+ return this.name + ': ' + this.message;
+};
+
diff --git a/proton-c/bindings/javascript/message.js b/proton-c/bindings/javascript/message.js
new file mode 100644
index 0000000..564cc6e
--- /dev/null
+++ b/proton-c/bindings/javascript/message.js
@@ -0,0 +1,848 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*****************************************************************************/
+/* */
+/* Message */
+/* */
+/*****************************************************************************/
+
+/**
+ * Constructs a proton.Message instance.
+ * @classdesc This class is a mutable holder of message content that may be used
+ * to generate and encode or decode and access AMQP formatted message data.
+ * @constructor proton.Message
+ * @property {object} instructions delivery instructions for the message.
+ * @property {object} annotations infrastructure defined message annotations.
+ * @property {object} properties application defined message properties.
+ * @property {object} body message body as a native JavaScript Object.
+ * @property {object} data message body as a proton.Data Object.
+ */
+Module['Message'] = function() { // Message Constructor.
+ this._message = _pn_message();
+ this._id = new Data(_pn_message_id(this._message));
+ this._correlationId = new Data(_pn_message_correlation_id(this._message));
+
+ // ************************* Public properties ****************************
+
+ this['instructions'] = null;
+ this['annotations'] = null;
+
+ // Intitialise with an empty Object so we can set properties in a natural way.
+ // message.properties.prop1 = "foo";
+ // message.properties.prop2 = "bar";
+ this['properties'] = {};
+
+ this['body'] = null;
+ this['data'] = null;
+};
+
+// Expose constructor as package scope variable to make internal calls less verbose.
+var Message = Module['Message'];
+
+// Expose prototype as a variable to make method declarations less verbose.
+var _Message_ = Message.prototype;
+
+// ************************** Class properties ********************************
+
+Message['DEFAULT_PRIORITY'] = 4; /** Default priority for messages.*/
+
+// ************************* Protected methods ********************************
+
+// We use the dot notation rather than associative array form for protected
+// methods so they are visible to this "package", but the Closure compiler will
+// minify and obfuscate names, effectively making a defacto "protected" method.
+
+/**
+ * This helper method checks the supplied error code, converts it into an
+ * exception and throws the exception. This method will try to use the message
+ * populated in pn_message_error(), if present, but if not it will fall
+ * back to using the basic error code rendering from pn_code().
+ * @param code the error code to check.
+ */
+_Message_._check = function(code) {
+ if (code < 0) {
+ var errno = this['getErrno']();
+ var message = errno ? this['getError']() : Pointer_stringify(_pn_code(code));
+
+ throw new Module['MessageError'](message);
+ } else {
+ return code;
+ }
+};
+
+/**
+ * Encode the Message prior to sending on the wire.
+ */
+_Message_._preEncode = function() {
+ // A Message Object may be reused so we create new Data instances and clear
+ // the state for them each time put() gets called.
+ var inst = new Data(_pn_message_instructions(this._message));
+ var ann = new Data(_pn_message_annotations(this._message));
+ var props = new Data(_pn_message_properties(this._message));
+ var body = new Data(_pn_message_body(this._message));
+
+ inst.clear();
+ if (this['instructions']) {
+ inst['putObject'](this['instructions']);
+ }
+
+ ann.clear();
+ if (this['annotations']) {
+ ann['putObject'](this['annotations']);
+ }
+
+ props.clear();
+ if (this['properties']) {
+ props['putObject'](this['properties']);
+ }
+
+ body.clear();
+ if (this['body']) {
+ var contentType = this['getContentType']();
+ if (contentType) {
+ var value = this['body'];
+ if (contentType === 'application/json' && JSON) { // Optionally encode body as JSON.
+ var json = JSON.stringify(value);
+ value = new Data['Binary'](json);
+ } else if (!(value instanceof Data['Binary'])) { // Construct a Binary from the body
+ value = new Data['Binary'](value);
+ }
+ // As content-type is set we send as an opaque AMQP data section.
+ this['setInferred'](true);
+ body['putBINARY'](value);
+ } else { // By default encode body using the native AMQP type system.
+ this['setInferred'](false);
+ body['putObject'](this['body']);
+ }
+ }
+};
+
+/**
+ * Decode the Message after receiving off the wire.
+ * @param {boolean} decodeBinaryAsString if set decode any AMQP Binary payload
+ * objects as strings. This can be useful as the data in Binary objects
+ * will be overwritten with subsequent calls to get, so they must be
+ * explicitly copied. Needless to say it is only safe to set this flag if
+ * you know that the data you are dealing with is actually a string, for
+ * example C/C++ applications often seem to encode strings as AMQP binary,
+ * a common cause of interoperability problems.
+ */
+_Message_._postDecode = function(decodeBinaryAsString) {
+ var inst = new Data(_pn_message_instructions(this._message));
+ var ann = new Data(_pn_message_annotations(this._message));
+ var props = new Data(_pn_message_properties(this._message));
+ var body = new Data(_pn_message_body(this._message), decodeBinaryAsString);
+
+ if (inst.next()) {
+ this['instructions'] = inst['getObject']();
+ } else {
+ this['instructions'] = {};
+ }
+
+ if (ann.next()) {
+ this['annotations'] = ann['getObject']();
+ } else {
+ this['annotations'] = {};
+ }
+
+ if (props.next()) {
+ this['properties'] = props['getObject']();
+ } else {
+ this['properties'] = {};
+ }
+
+ if (body.next()) {
+ this['data'] = body;
+ this['body'] = body['getObject']();
+ var contentType = this['getContentType']();
+ if (contentType) {
+ if (contentType === 'application/json' && JSON) {
+ var json = this['body'].toString(); // Convert Binary to String.
+ this['body'] = JSON.parse(json);
+ } else if (contentType.indexOf('text/') === 0) { // It's a text/* MIME type
+ this['body'] = this['body'].toString(); // Convert Binary to String.
+ }
+ }
+ } else {
+ this['data'] = null;
+ this['body'] = null;
+ }
+};
+
+// *************************** Public methods *********************************
+
+/**
+ * Free the Message.
+ * <p>
+ * N.B. This method has to be called explicitly in JavaScript as we can't
+ * intercept finalisers, so we need to remember to free before removing refs.
+ * @method free
+ * @memberof! proton.Message#
+ */
+_Message_['free'] = function() {
+ _pn_message_free(this._message);
+};
+
+/**
+ * @method getErrno
+ * @memberof! proton.Message#
+ * @returns {number the most recent error message code.
+ */
+_Message_['getErrno'] = function() {
+ return _pn_message_errno(this._message);
+};
+
+/**
+ * @method getError
+ * @memberof! proton.Message#
+ * @returns {string} the most recent error message as a String.
+ */
+_Message_['getError'] = function() {
+ return Pointer_stringify(_pn_error_text(_pn_message_error(this._message)));
+};
+
+/**
+ * Clears the contents of the Message. All fields will be reset to their default values.
+ * @method clear
+ * @memberof! proton.Message#
+ */
+_Message_['clear'] = function() {
+ _pn_message_clear(this._message);
+ this['instructions'] = null;
+ this['annotations'] = null;
+ this['properties'] = {};
+ this['body'] = null;
+ this['data'] = null;
+};
+
+/**
+ * Get the inferred flag for a message.
+ * <p>
+ * The inferred flag for a message indicates how the message content
+ * is encoded into AMQP sections. If inferred is true then binary and
+ * list values in the body of the message will be encoded as AMQP DATA
+ * and AMQP SEQUENCE sections, respectively. If inferred is false,
+ * then all values in the body of the message will be encoded as AMQP
+ * VALUE sections regardless of their type. Use
+ * {@link proton.Message.setInferred} to set the value.
+ * @method isInferred
+ * @memberof! proton.Message#
+ * @returns {boolean} true iff the inferred flag for the message is set.
+ */
+_Message_['isInferred'] = function() {
+ return (_pn_message_is_inferred(this._message) > 0);
+};
+
+/**
+ * Set the inferred flag for a message. See {@link proton.Message.isInferred}
+ * for a description of what the inferred flag is.
+ * @method setInferred
+ * @memberof! proton.Message#
+ * @param {boolean} inferred the new value of the inferred flag.
+ */
+_Message_['setInferred'] = function(inferred) {
+ this._check(_pn_message_set_inferred(this._message, inferred));
+};
+
+/**
+ * Get the durable flag for a message.
+ * <p>
+ * The durable flag indicates that any parties taking responsibility
+ * for the message must durably store the content. Use
+ * {@link proton.Message.setDurable} to set the value.
+ * @method isDurable
+ * @memberof! proton.Message#
+ * @returns {boolean} true iff the durable flag for the message is set.
+ */
+_Message_['isDurable'] = function() {
+ return (_pn_message_is_durable(this._message) > 0);
+};
+
+/**
+ * Set the durable flag for a message. See {@link proton.Message.isDurable}
+ * for a description of what the durable flag is.
+ * @method setDurable
+ * @memberof! proton.Message#
+ * @param {boolean} durable the new value of the durable flag.
+ */
+_Message_['setDurable'] = function(durable) {
+ this._check(_pn_message_set_durable(this._message, durable));
+};
+
+/**
+ * Get the priority for a message.
+ * <p>
+ * The priority of a message impacts ordering guarantees. Within a
+ * given ordered context, higher priority messages may jump ahead of
+ * lower priority messages. Priority range is 0..255
+ * @method getPriority
+ * @memberof! proton.Message#
+ * @returns {number} the priority of the Message.
+ */
+_Message_['getPriority'] = function() {
+ return _pn_message_get_priority(this._message) & 0xFF; // & 0xFF converts to unsigned.
+};
+
+/**
+ * Set the priority of the Message. See {@link proton.Message.getPriority}
+ * for details on message priority.
+ * @method setPriority
+ * @memberof! proton.Message#
+ * @param {number} priority the address we want to send the Message to.
+ */
+_Message_['setPriority'] = function(priority) {
+ this._check(_pn_message_set_priority(this._message, priority));
+};
+
+/**
+ * Get the ttl for a message.
+ * <p>
+ * The ttl for a message determines how long a message is considered
+ * live. When a message is held for retransmit, the ttl is
+ * decremented. Once the ttl reaches zero, the message is considered
+ * dead. Once a message is considered dead it may be dropped. Use
+ * {@link proton.Message.setTTL} to set the ttl for a message.
+ * @method getTTL
+ * @memberof! proton.Message#
+ * @returns {number} the ttl in milliseconds.
+ */
+_Message_['getTTL'] = function() {
+ return _pn_message_get_ttl(this._message);
+};
+
+/**
+ * Set the ttl for a message. See {@link proton.Message.getTTL}
+ * for a detailed description of message ttl.
+ * @method setTTL
+ * @memberof! proton.Message#
+ * @param {number} ttl the new value for the message ttl in milliseconds.
+ */
+_Message_['setTTL'] = function(ttl) {
+ this._check(_pn_message_set_ttl(this._message, ttl));
+};
+
+/**
+ * Get the first acquirer flag for a message.
+ * <p>
+ * When set to true, the first acquirer flag for a message indicates
+ * that the recipient of the message is the first recipient to acquire
+ * the message, i.e. there have been no failed delivery attempts to
+ * other acquirers. Note that this does not mean the message has not
+ * been delivered to, but not acquired, by other recipients.
+ * @method isFirstAcquirer
+ * @memberof! proton.Message#
+ * @returns {boolean} true iff the first acquirer flag for the message is set.
+ */
+_Message_['isFirstAcquirer'] = function() {
+ return (_pn_message_is_first_acquirer(this._message) > 0);
+};
+
+/**
+ * Set the first acquirer flag for a message. See {@link proton.Message.isFirstAcquirer}
+ * for details on the first acquirer flag.
+ * @method setFirstAcquirer
+ * @memberof! proton.Message#
+ * @param {boolean} first the new value of the first acquirer flag.
+ */
+_Message_['setFirstAcquirer'] = function(first) {
+ this._check(_pn_message_set_first_acquirer(this._message, first));
+};
+
+/**
+ * Get the delivery count for a message.
+ * <p>
+ * The delivery count field tracks how many attempts have been made to
+ * deliver a message. Use {@link proton.Message.setDeliveryCount} to set
+ * the delivery count for a message.
+ * @method getDeliveryCount
+ * @memberof! proton.Message#
+ * @returns {number} the delivery count for the message.
+ */
+_Message_['getDeliveryCount'] = function() {
+ return _pn_message_get_delivery_count(this._message);
+};
+
+/**
+ * Set the delivery count for a message. See {@link proton.Message.getDeliveryCount}
+ * for details on what the delivery count means.
+ * @method setDeliveryCount
+ * @memberof! proton.Message#
+ * @param {number} count the new delivery count.
+ */
+_Message_['setDeliveryCount'] = function(count) {
+ this._check(_pn_message_set_delivery_count(this._message, count));
+};
+
+/**
+ * Get the id for a message.
+ * <p>
+ * The message id provides a globally unique identifier for a message.
+ * A message id can be an a string, an unsigned long, a uuid or a binary value.
+ * @method getID
+ * @memberof! proton.Message#
+ * @returns {(number|string|proton.Data.Long|proton.Data.Uuid|proton.Data.Binary)} the message id.
+ */
+_Message_['getID'] = function() {
+ return this._id['getObject']();
+};
+
+/**
+ * Set the id for a message. See {@link proton.Message.getID}
+ * for more details on the meaning of the message id. Note that only string,
+ * unsigned long, uuid, or binary values are permitted.
+ * @method setID
+ * @memberof! proton.Message#
+ * @param {(number|string|proton.Data.Long|proton.Data.Uuid|proton.Data.Binary)} id the
+ * new value of the message id.
+ */
+_Message_['setID'] = function(id) {
+ this._id['rewind']();
+ if (Data.isNumber(id)) {
+ this._id['putULONG'](id);
+ } else {
+ this._id['putObject'](id);
+ }
+};
+
+/**
+ * Get the user id of the message creator.
+ * <p>
+ * The underlying raw data of the returned {@link proton.Data.Binary} will be
+ * valid until any one of the following operations occur:
+ * <pre>
+ * - {@link proton.Message.free}
+ * - {@link proton.Message.clear}
+ * - {@link proton.Message.setUserID}
+ * </pre>
+ * @method getUserID
+ * @memberof! proton.Message#
+ * @returns {proton.Data.Binary} the message's user id.
+ */
+_Message_['getUserID'] = function() {
+ var sp = Runtime.stackSave();
+ // The implementation here is a bit "quirky" due to some low-level details
+ // of the interaction between emscripten and LLVM and the use of pn_bytes.
+ // The JavaScript code below is basically a binding to:
+ //
+ // pn_bytes_t bytes = pn_message_get_user_id(message);
+
+ // Here's the quirky bit, pn_message_get_user_id actually returns pn_bytes_t
+ // *by value* but the low-level code handles this *by pointer* so we first
+ // need to allocate 8 bytes storage for {size, start} on the emscripten stack
+ // and then we pass the pointer to that storage as the first parameter to the
+ // compiled pn_message_get_user_id.
+ var bytes = allocate(8, 'i8', ALLOC_STACK);
+ _pn_message_get_user_id(bytes, this._message);
+
+ // The bytes variable is really of type pn_bytes_t* so we use emscripten's
+ // getValue() call to retrieve the size and then the start pointer.
+ var size = getValue(bytes, 'i32');
+ var start = getValue(bytes + 4, '*');
+
+ // Create a proton.Data.Binary from the pn_bytes_t information.
+ var binary = new Data['Binary'](size, start);
+
+ // Tidy up the memory that we allocated on emscripten's stack.
+ Runtime.stackRestore(sp);
+
+ return binary;
+};
+
+/**
+ * Set the user id for a message. This method takes a {@link proton.Data.Binary}
+ * consuming the underlying raw data in the process. For convenience this method
+ * also accepts a {@link proton.Data.Uuid}, number or string, converting them to a
+ * Binary internally. N.B. getUserID always returns a {@link proton.Data.Binary}
+ * even if a string or {@link proton.Data.Uuid} has been passed to setUserID.
+ * @method setUserID
+ * @memberof! proton.Message#
+ * @param {(string|proton.Data.Uuid)} id the new user id for the message.
+ */
+_Message_['setUserID'] = function(id) {
+ // If the id parameter is a proton.Data.Binary use it otherwise create a Binary
+ // using the string form of the parameter that was passed.
+ id = (id instanceof Data['Binary']) ? id : new Data['Binary']('' + id);
+
+ var sp = Runtime.stackSave();
+ // The implementation here is a bit "quirky" due to some low-level details
+ // of the interaction between emscripten and LLVM and the use of pn_bytes.
+ // The JavaScript code below is basically a binding to:
+ //
+ // pn_message_set_user_id(message, pn_bytes(id.size, id.start));
+
+ // Here's the quirky bit, pn_bytes actually returns pn_bytes_t *by value* but
+ // the low-level code handles this *by pointer* so we first need to allocate
+ // 8 bytes storage for {size, start} on the emscripten stack and then we
+ // pass the pointer to that storage as the first parameter to the pn_bytes.
+ var bytes = allocate(8, 'i8', ALLOC_STACK);
+ _pn_bytes(bytes, id.size, id.start);
+
+ // The compiled pn_message_set_user_id takes the pn_bytes_t by reference not value.
+ this._check(_pn_message_set_user_id(this._message, bytes));
+
+ // After calling _pn_message_set_user_id the underlying Message object "owns" the
+ // binary data, so we can call free on the proton.Data.Binary instance to
+ // release any storage it has acquired back to the emscripten heap.
+ id['free']();
+ Runtime.stackRestore(sp);
+};
+
+/**
+ * Get the address for a message.
+ * @method getAddress
+ * @memberof! proton.Message#
+ * @returns {string} the address of the Message.
+ */
+_Message_['getAddress'] = function() {
+ return Pointer_stringify(_pn_message_get_address(this._message));
+};
+
+/**
+ * Set the address of the Message.
+ * @method setAddress
+ * @memberof! proton.Message#
+ * @param {string} address the address we want to send the Message to.
+ */
+_Message_['setAddress'] = function(address) {
+ var sp = Runtime.stackSave();
+ this._check(_pn_message_set_address(this._message, allocate(intArrayFromString(address), 'i8', ALLOC_STACK)));
+ Runtime.stackRestore(sp);
+};
+
+/**
+ * Get the subject for a message.
+ * @method getSubject
+ * @memberof! proton.Message#
+ * @returns {string} the subject of the Message.
+ */
+_Message_['getSubject'] = function() {
+ return Pointer_stringify(_pn_message_get_subject(this._message));
+};
+
+/**
+ * Set the subject of the Message.
+ * @method setSubject
+ * @memberof! proton.Message#
+ * @param {string} subject the subject we want to set for the Message.
+ */
+_Message_['setSubject'] = function(subject) {
+ var sp = Runtime.stackSave();
+ this._check(_pn_message_set_subject(this._message, allocate(intArrayFromString(subject), 'i8', ALLOC_STACK)));
+ Runtime.stackRestore(sp);
+};
+
+/**
+ * Get the reply to for a message.
+ * @method getReplyTo
+ * @memberof! proton.Message#
+ * @returns {string} the reply to of the Message.
+ */
+_Message_['getReplyTo'] = function() {
+ return Pointer_stringify(_pn_message_get_reply_to(this._message));
+};
+
+/**
+ * Set the reply to for a message.
+ * @method setReplyTo
+ * @memberof! proton.Message#
+ * @param {string} reply the reply to we want to set for the Message.
+ */
+_Message_['setReplyTo'] = function(reply) {
+ var sp = Runtime.stackSave();
+ this._check(_pn_message_set_reply_to(this._message, allocate(intArrayFromString(reply), 'i8', ALLOC_STACK)));
+ Runtime.stackRestore(sp);
+};
+
+/**
+ * Get the correlation id for a message.
+ * <p>
+ * A correlation id can be an a string, an unsigned long, a uuid or a binary value.
+ * @method getCorrelationID
+ * @memberof! proton.Message#
+ * @returns {(number|string|proton.Data.Long|proton.Data.Uuid|proton.Data.Binary)} the message id.
+ */
+_Message_['getCorrelationID'] = function() {
+ return this._correlationId['getObject']();
+};
+
+/**
+ * Set the correlation id for a message. See {@link proton.Message.getCorrelationID}
+ * for more details on the meaning of the correlation id. Note that only string,
+ * unsigned long, uuid, or binary values are permitted.
+ * @method setCorrelationID
+ * @memberof! proton.Message#
+ * @param {(number|string|proton.Data.Long|proton.Data.Uuid|proton.Data.Binary)} id the
+ * new value of the correlation id.
+ */
+_Message_['setCorrelationID'] = function(id) {
+ this._correlationId['rewind']();
+ if (Data.isNumber(id)) {
+ this._correlationId['putULONG'](id);
+ } else {
+ this._correlationId['putObject'](id);
+ }
+};
+
+/**
+ * Get the content type for a message.
+ * @method getContentType
+ * @memberof! proton.Message#
+ * @returns {string} the content type of the Message.
+ */
+_Message_['getContentType'] = function() {
+ return Pointer_stringify(_pn_message_get_content_type(this._message));
+};
+
+/**
+ * Set the content type for a message.
+ * @method setContentType
+ * @memberof! proton.Message#
+ * @param {string} type the content type we want to set for the Message.
+ */
+_Message_['setContentType'] = function(type) {
+ var sp = Runtime.stackSave();
+ this._check(_pn_message_set_content_type(this._message, allocate(intArrayFromString(type), 'i8', ALLOC_STACK)));
+ Runtime.stackRestore(sp);
+};
+
+/**
+ * Get the content encoding for a message.
+ * @method getContentEncoding
+ * @memberof! proton.Message#
+ * @returns {string} the content encoding of the Message.
+ */
+_Message_['getContentEncoding'] = function() {
+ return Pointer_stringify(_pn_message_get_content_encoding(this._message));
+};
+
+/**
+ * Set the content encoding for a message.
+ * @method setContentEncoding
+ * @memberof! proton.Message#
+ * @param {string} encoding the content encoding we want to set for the Message.
+ */
+_Message_['setContentEncoding'] = function(encoding) {
+ var sp = Runtime.stackSave();
+ this._check(_pn_message_set_content_encoding(this._message, allocate(intArrayFromString(encoding), 'i8', ALLOC_STACK)));
+ Runtime.stackRestore(sp);
+};
+
+/**
+ * Get the expiry time for a message.
+ * A zero value for the expiry time indicates that the message will
+ * never expire. This is the default value.
+ * @method getExpiryTime
+ * @memberof! proton.Message#
+ * @returns {Date} the expiry time for the message.
+ */
+_Message_['getExpiryTime'] = function() {
+ // Getting the timestamp is a little tricky as it is a 64 bit number. The way
+ // emscripten handles this is to return the low 32 bits directly and pass
+ // the high 32 bits via the tempRet0 variable. We use Data.Long to hold
+ // the 64 bit number and Data.Long.toNumber() to convert it back into a
+ // JavaScript number.
+ var low = _pn_message_get_expiry_time(this._message);
+ var high = Runtime.getTempRet0();
+ var long = new Data.Long(low, high);
+ long = long.toNumber();
+ return new Date(long);
+};
+
+/**
+ * Set the expiry time for a message.
+ * @method setExpiryTime
+ * @memberof! proton.Message#
+ * @param {(number|Date)} time the new expiry time for the message.
+ */
+_Message_['setExpiryTime'] = function(time) {
+ // Note that a timestamp is a 64 bit number so we have to use a proton.Data.Long.
+ var timestamp = Data.Long.fromNumber(time.valueOf());
+ this._check(_pn_message_set_expiry_time(this._message, timestamp.getLowBitsUnsigned(), timestamp.getHighBits()));
+};
+
+/**
+ * Get the creation time for a message.
+ * A zero value for the creation time indicates that the creation time
+ * has not been set. This is the default value.
+ * @method getCreationTime
+ * @memberof! proton.Message#
+ * @returns {Date} the creation time for the message.
+ */
+_Message_['getCreationTime'] = function() {
+ // Getting the timestamp is a little tricky as it is a 64 bit number. The way
+ // emscripten handles this is to return the low 32 bits directly and pass
+ // the high 32 bits via the tempRet0 variable. We use Data.Long to hold
+ // the 64 bit number and Data.Long.toNumber() to convert it back into a
+ // JavaScript number.
+ var low = _pn_message_get_creation_time(this._message);
+ var high = Runtime.getTempRet0();
+ var long = new Data.Long(low, high);
+ long = long.toNumber();
+ return new Date(long);
+};
+
+/**
+ * Set the creation time for a message.
+ * @method setCreationTime
+ * @memberof! proton.Message#
+ * @param {(number|Date)} time the new creation time for the message.
+ */
+_Message_['setCreationTime'] = function(time) {
+ // Note that a timestamp is a 64 bit number so we have to use a proton.Data.Long.
+ var timestamp = Data.Long.fromNumber(time.valueOf());
+ this._check(_pn_message_set_creation_time(this._message, timestamp.getLowBitsUnsigned(), timestamp.getHighBits()));
+};
+
+/**
+ * Get the group id for a message.
+ * @method getGroupID
+ * @memberof! proton.Message#
+ * @returns {string} the group id of the Message.
+ */
+_Message_['getGroupID'] = function() {
+ return Pointer_stringify(_pn_message_get_group_id(this._message));
+};
+
+/**
+ * Set the group id for a message.
+ * @method setGroupID
+ * @memberof! proton.Message#
+ * @param {string} id the group id we want to set for the Message.
+ */
+_Message_['setGroupID'] = function(id) {
+ var sp = Runtime.stackSave();
+ this._check(_pn_message_set_group_id(this._message, allocate(intArrayFromString(id), 'i8', ALLOC_STACK)));
+ Runtime.stackRestore(sp);
+};
+
+/**
+ * Get the group sequence for a message.
+ * <p>
+ * The group sequence of a message identifies the relative ordering of
+ * messages within a group. The default value for the group sequence
+ * of a message is zero.
+ * @method getGroupSequence
+ * @memberof! proton.Message#
+ * @returns {number} the group sequence for the message.
+ */
+_Message_['getGroupSequence'] = function() {
+ return _pn_message_get_group_sequence(this._message);
+};
+
+/**
+ * Set the group sequence for a message. See {@link proton.Message.getGroupSequence}
+ * for details on what the group sequence means.
+ * @method setGroupSequence
+ * @memberof! proton.Message#
+ * @param {number} n the new group sequence for the message.
+ */
+_Message_['setGroupSequence'] = function(n) {
+ this._check(_pn_message_set_group_sequence(this._message, n));
+};
+
+/**
+ * Get the reply to group id for a message.
+ * @method getReplyToGroupID
+ * @memberof! proton.Message#
+ * @returns {string} the reply to group id of the Message.
+ */
+_Message_['getReplyToGroupID'] = function() {
+ return Pointer_stringify(_pn_message_get_reply_to_group_id(this._message));
+};
+
+/**
+ * Set the reply to group id for a message.
+ * @method setReplyToGroupID
+ * @memberof! proton.Message#
+ * @param {string} id the reply to group id we want to set for the Message.
+ */
+_Message_['setReplyToGroupID'] = function(id) {
+ var sp = Runtime.stackSave();
+ this._check(_pn_message_set_reply_to_group_id(this._message, allocate(intArrayFromString(id), 'i8', ALLOC_STACK)));
+ Runtime.stackRestore(sp);
+};
+
+/**
+ * The following methods are marked as deprecated and are not implemented.
+ * pn_message_get_format()
+ * pn_message_set_format()
+ * pn_message_load()
+ * pn_message_load_data()
+ * pn_message_load_text()
+ * pn_message_load_amqp()
+ * pn_message_load_json()
+ * pn_message_save()
+ * pn_message_save_data()
+ * pn_message_save_text()
+ * pn_message_save_amqp()
+ * pn_message_save_json()
+ * pn_message_data()
+ */
+
+/**
+ * Return a Binary representation of the message encoded in AMQP format. N.B. the
+ * returned {@link proton.Data.Binary} "owns" the underlying raw data and is thus
+ * responsible for freeing it or passing it to a method that consumes a Binary
+ * such as {@link proton.Message.decode}.
+ * @method encode
+ * @memberof! proton.Message#
+ * @returns {proton.Data.Binary} a representation of the message encoded in AMQP format.
+ */
+_Message_['encode'] = function() {
+ this._preEncode();
+ var size = 1024;
+ while (true) {
+ setValue(size, size, 'i32'); // Set pass by reference variable.
+ var bytes = _malloc(size); // Allocate storage from emscripten heap.
+ var err = _pn_message_encode(this._message, bytes, size);
+ var size = getValue(size, 'i32'); // Dereference the real size value;
+
+ if (err === Module['Error']['OVERFLOW']) {
+ _free(bytes);
+ size *= 2;
+ } else if (err >= 0) {
+ return new Data['Binary'](size, bytes);
+ } else {
+ _free(bytes);
+ this._check(err);
+ return;
+ }
+ }
+};
+
+/**
+ * Decodes and loads the message content from supplied Binary AMQP data N.B.
+ * this method "consumes" data from a {@link proton.Data.Binary} in other words
+ * it takes responsibility for the underlying data and frees the raw data from
+ * the Binary.
+ * @method decode
+ * @memberof! proton.Message#
+ * @param {proton.Data.Binary} encoded the AMQP encoded binary message.
+ */
+_Message_['decode'] = function(encoded) {
+ var err = _pn_message_decode(this._message, encoded.start, encoded.size);
+ encoded['free'](); // Free the original Binary.
+ if (err >= 0) {
+ this._postDecode();
+ }
+ this._check(err);
+};
+
diff --git a/proton-c/bindings/javascript/messenger.js b/proton-c/bindings/javascript/messenger.js
new file mode 100644
index 0000000..e2f4418
--- /dev/null
+++ b/proton-c/bindings/javascript/messenger.js
@@ -0,0 +1,810 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*****************************************************************************/
+/* */
+/* Messenger */
+/* */
+/*****************************************************************************/
+
+/**
+ * Constructs a proton.Messenger instance giving it an (optional) name. If name
+ * is supplied that will be used as the name of the Messenger, otherwise a UUID
+ * will be used. The Messenger is initialised to non-blocking mode as it makes
+ * little sense to have blocking behaviour in a JavaScript implementation.
+ * @classdesc The {@link proton.Messenger} class defines a high level interface for sending
+ * and receiving {@link proton.Message}. Every {@link proton.Messenger} contains a
+ * single logical queue of incoming messages and a single logical queue
+ * of outgoing messages. These messages in these queues may be destined
+ * for, or originate from, a variety of addresses.
+ * <p>
+ * The messenger interface is single-threaded.
+ * <pre>
+ * Address Syntax
+ * ==============
+ * </pre>
+ * An address has the following form:
+ * <pre>
+ * [ amqp[s]:// ] [user[:password]@] domain [/[name]]
+ * </pre>
+ * Where domain can be one of:
+ * <pre>
+ * host | host:port | ip | ip:port | name
+ * </pre>
+ * The following are valid examples of addresses:
+ * <pre>
+ * - example.org
+ * - example.org:1234
+ * - amqp://example.org
+ * - amqps://example.org
+ * - example.org/incoming
+ * - amqps://example.org/outgoing
+ * - amqps://fred:trustno1@example.org
+ * - 127.0.0.1:1234
+ * - amqps://127.0.0.1:1234
+ *
+ * Sending & Receiving Messages
+ * ============================
+ * </pre>
+ * The {@link proton.Messenger} class works in conjuction with the {@link proton.Message} class.
+ * The {@link proton.Message} class is a mutable holder of message content.
+ * <p>
+ * The put method copies its Message to the outgoing queue, and may
+ * send queued messages if it can do so without blocking.
+ * <pre>
+ * var message = new proton.Message();
+ * for (var i = 0; i < 3; i++) {
+ * message.setAddress("amqp://host/queue");
+ * message.setSubject = ("Hello World " + i);
+ * messenger.put(message);
+ * }
+ * </pre>
+ * Similarly, the recv method receives messages into the incoming
+ * queue. It may receive fewer than the requested number. The get method pops the
+ * eldest Message off the incoming queue and copies it into the Message
+ * object that you supply.
+ * <pre>
+ * var message = new proton.Message();
+ * messenger.recv(10);
+ * while (messenger.incoming() > 0) {
+ * messenger.get(message);
+ * console.log(message.getSubject());
+ * }
+ * Hello World 0
+ * Hello World 1
+ * Hello World 2
+ * </pre>
+ *
+ * @constructor proton.Messenger
+ * @param {string} name the name of this Messenger instance.
+ */
+Module['Messenger'] = function(name) { // Messenger Constructor.
+ /**
+ * The emscripten idiom below is used in a number of places in the JavaScript
+ * bindings to map JavaScript Strings to C style strings. ALLOC_STACK will
+ * increase the stack and place the item there. When the stack is next restored
+ * (by calling Runtime.stackRestore()), that memory will be automatically
+ * freed. In C code compiled by emscripten saving and restoring of the stack
+ * is automatic, but if we want to us ALLOC_STACK from native JavaScript we
+ * need to explicitly save and restore the stack using Runtime.stackSave()
+ * and Runtime.stackRestore() or we will leak emscripten heap memory.
+ * See https://github.com/kripken/emscripten/wiki/Interacting-with-code
+ * The _pn_messenger constructor copies the char* passed to it.
+ */
+ var sp = Runtime.stackSave();
+ this._messenger = _pn_messenger(name ? allocate(intArrayFromString(name), 'i8', ALLOC_STACK) : 0);
+ Runtime.stackRestore(sp);
+
+ /**
+ * Initiate Messenger non-blocking mode. For JavaScript we make this the
+ * default behaviour and don't export this method because JavaScript is
+ * fundamentally an asynchronous non-blocking execution environment.
+ */
+ _pn_messenger_set_blocking(this._messenger, false);
+
+ // Subscriptions that haven't yet completed, used for managing subscribe events.
+ this._pendingSubscriptions = [];
+
+ // Used in the Event registration mechanism (in the 'on' and 'emit' methods).
+ this._callbacks = {};
+
+ // This call ensures that the emscripten network callback functions are initialised.
+ Module.EventDispatch.registerMessenger(this);
+};
+
+Module['Messenger'].PN_CUMULATIVE = 0x1; // Protected Class attribute.
+
+// Expose prototype as a variable to make method declarations less verbose.
+var _Messenger_ = Module['Messenger'].prototype;
+
+// ************************* Protected methods ********************************
+
+// We use the dot notation rather than associative array form for protected
+// methods so they are visible to this "package", but the Closure compiler will
+// minify and obfuscate names, effectively making a defacto "protected" method.
+
+/**
+ * This helper method checks the supplied error code, converts it into an
+ * exception and throws the exception. This method will try to use the message
+ * populated in pn_messenger_error(), if present, but if not it will fall
+ * back to using the basic error code rendering from pn_code().
+ * @param {number} code the error code to check.
+ */
+_Messenger_._check = function(code) {
+ if (code < 0 && code !== Module['Error']['INPROGRESS']) {
+ var errno = this['getErrno']();
+ var message = errno ? this['getError']() : Pointer_stringify(_pn_code(code));
+ if (message !== 'PN_TIMEOUT') {
+ if (this._callbacks['error']) {
+console.log("emitting " + message);
+ this._emit('error', new Module['MessengerError'](message));
+ } else {
+console.log("throwing " + message);
+ throw new Module['MessengerError'](message);
+ }
+ }
+ }
+
+ return code;
+};
+
+/**
+ * Invokes the callbacks registered for a specified event.
+ * @method _emit
+ * @memberof! proton.Messenger#
+ * @param event {string} the event we want to emit.
+ * @param param {object} the parameter we'd like to pass to the event callback.
+ */
+_Messenger_._emit = function(event, param) {
+ var callbacks = this._callbacks[event];
+ if (callbacks) {
+ for (var i = 0; i < callbacks.length; i++) {
+ var callback = callbacks[i];
+ if ('function' === typeof callback) {
+ callback.call(this, param);
+ }
+ }
+ }
+};
+
+/**
+ * Checks any pending subscriptions and when a source address becomes available
+ * emit a subscription event passing the Subscription that triggered the event.
+ * Note that this doesn't seem to work for listen/bind style subscriptions,
+ * that is to say subscriptions of the form amqp://~0.0.0.0, don't know why?
+ * As a workaround the subscribe call emits a subscription event immediately for
+ * peer subscriptions to the local Messenger, this *should* be OK.
+ */
+_Messenger_._checkSubscriptions = function() {
+ // Check for completed subscriptions, and emit subscribe event.
+ var subscriptions = this._pendingSubscriptions;
+ if (subscriptions.length) {
+ var pending = []; // Array of any subscriptions that remain pending.
+ for (var j = 0; j < subscriptions.length; j++) {
+ subscription = subscriptions[j];
+ if (subscription['getAddress']()) {
+ this._emit('subscription', subscription);
+ } else {
+ pending.push(subscription);
+ }
+ }
+ this._pendingSubscriptions = pending;
+ }
+};
+
+
+// *************************** Public methods *****************************
+
+/**
+ * N.B. The following methods are not exported by the JavaScript Messenger
+ * binding for reasons described below.
+ *
+ * For these methods it is expected that security would be implemented via
+ * a secure WebSocket. TODO what happens if we decide to implement TCP sockets
+ * via Node.js net library. If we do that we may want to compile OpenSSL
+ * using emscripten and include these methods.
+ * pn_messenger_set_certificate()
+ * pn_messenger_get_certificate()
+ * pn_messenger_set_private_key()
+ * pn_messenger_get_private_key()
+ * pn_messenger_set_password()
+ * pn_messenger_get_password()
+ * pn_messenger_set_trusted_certificates()
+ * pn_messenger_get_trusted_certificates()
+ *
+ * For these methods the implementation is fairly meaningless because JavaScript
+ * is a fundamentally asynchronous non-blocking environment.
+ * pn_messenger_set_timeout()
+ * pn_messenger_set_blocking()
+ * pn_messenger_interrupt()
+ * pn_messenger_send() // Not sure if this is useful in JavaScript.
+ */
+
+/**
+ * Registers a listener callback for a specified event.
+ * @method on
+ * @memberof! proton.Messenger#
+ * @param {string} event the event we want to listen for.
+ * @param {function} callback the callback function to be registered for the specified event.
+ */
+_Messenger_['on'] = function(event, callback) {
+ if ('function' === typeof callback) {
+ if (!this._callbacks[event]) {
+ this._callbacks[event] = [];
+ }
+
+ this._callbacks[event].push(callback);
+ }
+};
+
+/**
+ * Removes a listener callback for a specified event.
+ * @method removeListener
+ * @memberof! proton.Messenger#
+ * @param {string} event the event we want to detach from.
+ * @param {function} callback the callback function to be removed for the specified event.
+ * if no callback is specified all callbacks are removed for the event.
+ */
+_Messenger_['removeListener'] = function(event, callback) {
+ if (callback) {
+ var callbacks = this._callbacks[event];
+ if ('function' === typeof callback && callbacks) {
+ // Search for the specified callback.
+ for (var i = 0; i < callbacks.length; i++) {
+ if (callback === callbacks[i]) {
+ // If we find the specified callback, delete it and return.
+ callbacks.splice(i, 1);
+ return;
+ }
+ }
+ }
+ } else {
+ // If we call remove with no callback specified we remove all callbacks.
+ delete this._callbacks[event];
+ }
+};
+
+/**
+ * Retrieves the name of a Messenger.
+ * @method getName
+ * @memberof! proton.Messenger#
+ * @returns {string} the name of the messenger.
+ */
+_Messenger_['getName'] = function() {
+ return Pointer_stringify(_pn_messenger_name(this._messenger));
+};
+
+/**
+ * Retrieves the timeout for a Messenger.
+ * @method getTimeout
+ * @memberof! proton.Messenger#
+ * @returns {number} zero because JavaScript is fundamentally non-blocking.
+ */
+_Messenger_['getTimeout'] = function() {
+ return 0;
+};
+
+/**
+ * Accessor for messenger blocking mode.
+ * @method isBlocking
+ * @memberof! proton.Messenger#
+ * @returns {boolean} false because JavaScript is fundamentally non-blocking.
+ */
+_Messenger_['isBlocking'] = function() {
+ return false;
+};
+
+/**
+ * Free the Messenger. This will close all connections that are managed
+ * by the Messenger. Call the stop method before destroying the Messenger.
+ * <p>
+ * N.B. This method has to be called explicitly in JavaScript as we can't
+ * intercept finalisers, so we need to remember to free before removing refs.
+ * @method free
+ * @memberof! proton.Messenger#
+ */
+_Messenger_['free'] = function() {
+ // This call ensures that the emscripten network callback functions are removed.
+ Module.EventDispatch.unregisterMessenger(this);
+ _pn_messenger_free(this._messenger);
+};
+
+/**
+ * @method getErrno
+ * @memberof! proton.Messenger#
+ * @returns {number} the most recent error message code.
+ */
+_Messenger_['getErrno'] = function() {
+ return _pn_messenger_errno(this._messenger);
+};
+
+/**
+ * @method getError
+ * @memberof! proton.Messenger#
+ * @returns {string} the most recent error message as a String.
+ */
+_Messenger_['getError'] = function() {
+ return Pointer_stringify(_pn_error_text(_pn_messenger_error(this._messenger)));
+};
+
+/**
+ * Returns the size of the outgoing window that was set with setOutgoingWindow.
+ * The default is 0.
+ * @method getOutgoingWindow
+ * @memberof! proton.Messenger#
+ * @returns {number} the outgoing window size.
+ */
+_Messenger_['getOutgoingWindow'] = function() {
+ return _pn_messenger_get_outgoing_window(this._messenger);
+};
+
+/**
+ * Sets the outgoing tracking window for the Messenger. The Messenger will
+ * track the remote status of this many outgoing deliveries after calling
+ * send. Defaults to zero.
+ * <p>
+ * A Message enters this window when you call put() with the Message.
+ * If your outgoing window size is n, and you call put() n+1 times, status
+ * information will no longer be available for the first Message.
+ * @method setOutgoingWindow
+ * @memberof! proton.Messenger#
+ * @param {number} window the size of the tracking window in messages.
+ */
+_Messenger_['setOutgoingWindow'] = function(window) {
+ _pn_messenger_set_outgoing_window(this._messenger, window);
+};
+
+/**
+ * Returns the size of the incoming window that was set with setIncomingWindow.
+ * The default is 0.
+ * @method getIncomingWindow
+ * @memberof! proton.Messenger#
+ * @returns {number} the incoming window size.
+ */
+_Messenger_['getIncomingWindow'] = function() {
+ return _pn_messenger_get_incoming_window(this._messenger);
+};
+
+/**
+ * Sets the incoming tracking window for the Messenger. The Messenger will
+ * track the remote status of this many incoming deliveries after calling
+ * send. Defaults to zero.
+ * <p>
+ * Messages enter this window only when you take them into your application
+ * using get(). If your incoming window size is n, and you get() n+1 messages
+ * without explicitly accepting or rejecting the oldest message, then the
+ * Message that passes beyond the edge of the incoming window will be assigned
+ * the default disposition of its link.
+ * @method setIncomingWindow
+ * @memberof! proton.Messenger#
+ * @param {number} window the size of the tracking window in messages.
+ */
+_Messenger_['setIncomingWindow'] = function(window) {
+ _pn_messenger_set_incoming_window(this._messenger, window);
+};
+
+/**
+ * Currently a no-op placeholder. For future compatibility, do not send or
+ * recv messages before starting the Messenger.
+ * @method start
+ * @memberof! proton.Messenger#
+ */
+_Messenger_['start'] = function() {
+ this._check(_pn_messenger_start(this._messenger));
+};
+
+/**
+ * Transitions the Messenger to an inactive state. An inactive Messenger
+ * will not send or receive messages from its internal queues. A Messenger
+ * should be stopped before being discarded to ensure a clean shutdown
+ * handshake occurs on any internally managed connections.
+ * <p>
+ * The Messenger may require some time to stop if it is busy, and in that
+ * case will return {@link proton.Error.INPROGRESS}. In that case, call isStopped
+ * to see if it has fully stopped.
+ * @method stop
+ * @memberof! proton.Messenger#
+ */
+_Messenger_['stop'] = function() {
+ _pn_messenger_stop(this._messenger);
+};
+
+/**
+ * Returns true iff a Messenger is in the stopped state.
+ * @method isStopped
+ * @memberof! proton.Messenger#
+ * @returns {boolean} true iff a Messenger is in the stopped state.
+ */
+_Messenger_['isStopped'] = function() {
+ return (_pn_messenger_stopped(this._messenger) > 0);
+};
+
+/**
+ * Subscribes the Messenger to messages originating from the
+ * specified source. The source is an address as specified in the
+ * Messenger introduction with the following addition. If the
+ * domain portion of the address begins with the '~' character, the
+ * Messenger will interpret the domain as host/port, bind to it,
+ * and listen for incoming messages. For example "~0.0.0.0",
+ * "amqp://~0.0.0.0", and "amqps://~0.0.0.0" will all bind to any
+ * local interface and listen for incoming messages with the last
+ * variant only permitting incoming SSL connections.
+ * @method subscribe
+ * @memberof! proton.Messenger#
+ * @param {string} source the source address we're subscribing to.
+ * @returns {Subscription} a subscription.
+ */
+_Messenger_['subscribe'] = function(source) {
+ if (!source) {
+ this._emit('error', new Module['SubscriptionError'](source, 'CONNECTION ERROR: Address not specified'));
+ } else {
+ return Module.EventDispatch.subscribe(this, source);
+ }
+};
+
+/**
+ * Places the content contained in the message onto the outgoing queue
+ * of the Messenger. This method will never block, however it will send any
+ * unblocked Messages in the outgoing queue immediately and leave any blocked
+ * Messages remaining in the outgoing queue. The outgoing property may be
+ * used to check the depth of the outgoing queue.
+ * <p>
+ * When the content in a given Message object is copied to the outgoing
+ * message queue, you may then modify or discard the Message object
+ * without having any impact on the content in the outgoing queue.
+ * <p>
+ * This method returns an outgoing tracker for the Message. The tracker
+ * can be used to determine the delivery status of the Message.
+ * @method put
+ * @memberof! proton.Messenger#
+ * @param {proton.Message} message a Message to send.
+ * @param {boolean} flush if this is set true or is undefined then messages are
+ * flushed (this is the default). If explicitly set to false then messages
+ * may not be sent immediately and might require an explicit call to work().
+ * This may be used to "batch up" messages and *may* be more efficient.
+ * @returns {proton.Data.Long} a tracker.
+ */
+_Messenger_['put'] = function(message, flush) {
+ flush = flush === false ? false : true; // Defaults to true if not explicitly specified.
+ message._preEncode();
+ this._check(_pn_messenger_put(this._messenger, message._message));
+
+ if (flush) {
+ Module.EventDispatch.flush();
+ }
+
+ // Getting the tracker is a little tricky as it is a 64 bit number. The way
+ // emscripten handles this is to return the low 32 bits directly and pass
+ // the high 32 bits via the tempRet0 variable. We use Data.Long to pass the
+ // low/high pair around to methods that require a tracker.
+ var low = _pn_messenger_outgoing_tracker(this._messenger);
+ var high = Runtime.getTempRet0();
+ return new Data.Long(low, high);
+};
+
+/**
+ * Gets the last known remote state of the delivery associated with the given tracker.
+ * @method status
+ * @memberof! proton.Messenger#
+ * @param {proton.Data.Long} tracker the tracker whose status is to be retrieved.
+ * @returns {proton.Status} one of None, PENDING, REJECTED, or ACCEPTED.
+ */
+_Messenger_['status'] = function(tracker) {
+ if (tracker == null) { // Use == not === to check for both null and undefined.
+ var low = _pn_messenger_outgoing_tracker(this._messenger);
+ var high = Runtime.getTempRet0();
+ tracker = new Data.Long(low, high);
+ }
+
+ return _pn_messenger_status(this._messenger, tracker.getLowBitsUnsigned(), tracker.getHighBits());
+};
+
+/**
+ * Checks if the delivery associated with the given tracker is still waiting to be sent.
+ * @method isBuffered
+ * @memberof! proton.Messenger#
+ * @param {proton.Data.Long} tracker the tracker identifying the delivery.
+ * @returns {boolean} true if delivery is still buffered.
+ */
+_Messenger_['isBuffered'] = function(tracker) {
+ if (tracker == null) { // Use == not === to check for both null and undefined.
+ var low = _pn_messenger_outgoing_tracker(this._messenger);
+ var high = Runtime.getTempRet0();
+ tracker = new Data.Long(low, high);
+ }
+
+ return (_pn_messenger_buffered(this._messenger, tracker.getLowBitsUnsigned(), tracker.getHighBits()) > 0);
+};
+
+/**
+ * Frees a Messenger from tracking the status associated with a given tracker.
+ * If you don't supply a tracker, all outgoing messages up to the most recent
+ * will be settled.
+ * @method settle
+ * @memberof! proton.Messenger#
+ * @param {proton.Data.Long} tracker the tracker identifying the delivery.
+ */
+_Messenger_['settle'] = function(tracker) {
+ // Getting the tracker is a little tricky as it is a 64 bit number. The way
+ // emscripten handles this is to return the low 32 bits directly and pass
+ // the high 32 bits via the tempRet0 variable. We use Data.Long to pass the
+ // low/high pair around to methods that require a tracker.
+ var flags = 0;
+ if (tracker == null) { // Use == not === to check for both null and undefined.
+ var low = _pn_messenger_outgoing_tracker(this._messenger);
+ var high = Runtime.getTempRet0();
+ tracker = new Data.Long(low, high);
+ flags = Module['Messenger'].PN_CUMULATIVE;
+ }
+
+ _pn_messenger_settle(this._messenger, tracker.getLowBitsUnsigned(), tracker.getHighBits(), flags);
+};
+
+/**
+ * Sends or receives any outstanding messages queued for a Messenger.
+ * For JavaScript the only timeout that makes sense is 0 (do not block).
+ * This method may also do I/O work other than sending and receiving messages.
+ * For example, closing connections after messenger.stop() has been called.
+ * @method work
+ * @memberof! proton.Messenger#
+ * @returns {boolean} true if there is work still to do, false otherwise.
+ */
+_Messenger_['work'] = function() {
+ return (this._check(_pn_messenger_work(this._messenger, 0)) > 0);
+};
+
+/**
+ * Receives up to limit messages into the incoming queue. If no value for limit
+ * is supplied, this call will receive as many messages as it can buffer internally.
+ * @method recv
+ * @memberof! proton.Messenger#
+ * @param {number} limit the maximum number of messages to receive or -1 to to receive
+ * as many messages as it can buffer internally.
+ */
+_Messenger_['recv'] = function(limit) {
+ _pn_messenger_recv(this._messenger, (limit ? limit : -1));
+};
+
+/**
+ * Returns the capacity of the incoming message queue of messenger. Note this
+ * count does not include those messages already available on the incoming queue.
+ * @method receiving
+ * @memberof! proton.Messenger#
+ * @returns {number} the message queue capacity.
+ */
+_Messenger_['receiving'] = function() {
+ return _pn_messenger_receiving(this._messenger);
+};
+
+/**
+ * Moves the message from the head of the incoming message queue into the
+ * supplied message object. Any content in the message will be overwritten.
+ * <p>
+ * A tracker for the incoming Message is returned. The tracker can later be
+ * used to communicate your acceptance or rejection of the Message.
+ * @method get
+ * @memberof! proton.Messenger#
+ * @param {proton.Message} message the destination message object. If no Message
+ * object is supplied, the Message popped from the head of the queue is discarded.
+ * @param {boolean} decodeBinaryAsString if set decode any AMQP Binary payload
+ * objects as strings. This can be useful as the data in Binary objects
+ * will be overwritten with subsequent calls to get, so they must be
+ * explicitly copied. Needless to say it is only safe to set this flag if
+ * you know that the data you are dealing with is actually a string, for
+ * example C/C++ applications often seem to encode strings as AMQP binary,
+ * a common cause of interoperability problems.
+ * @returns {proton.Data.Long} a tracker for the incoming Message.
+ */
+_Messenger_['get'] = function(message, decodeBinaryAsString) {
+ var impl = null;
+ if (message) {
+ impl = message._message;
+ }
+
+ _pn_messenger_get(this._messenger, impl);
+
+ if (message) {
+ message._postDecode(decodeBinaryAsString);
+ }
+
+ // Getting the tracker is a little tricky as it is a 64 bit number. The way
+ // emscripten handles this is to return the low 32 bits directly and pass
+ // the high 32 bits via the tempRet0 variable. We use Data.Long to pass the
+ // low/high pair around to methods that require a tracker.
+ var low = _pn_messenger_incoming_tracker(this._messenger);
+ var high = Runtime.getTempRet0();
+ return new Data.Long(low, high);
+};
+
+/**
+ * Returns the Subscription of the Message returned by the most recent call
+ * to get, or null if pn_messenger_get has not yet been called.
+ * @method incomingSubscription
+ * @memberof! proton.Messenger#
+ * @returns {Subscription} a Subscription or null if get has never been called
+ * for this Messenger.
+ */
+_Messenger_['incomingSubscription'] = function() {
+ var subscription = _pn_messenger_incoming_subscription(this._messenger);
+ if (subscription) {
+ return new Subscription(subscription);
+ } else {
+ return null;
+ }
+};
+
+/**
+ * Signal the sender that you have acted on the Message pointed to by the tracker.
+ * If no tracker is supplied, then all messages that have been returned by the
+ * get method are accepted, except those that have already been auto-settled
+ * by passing beyond your incoming window size.
+ * @method accept
+ * @memberof! proton.Messenger#
+ * @param {proton.Data.Long} tracker the tracker identifying the delivery.
+ */
+_Messenger_['accept'] = function(tracker) {
+ // Getting the tracker is a little tricky as it is a 64 bit number. The way
+ // emscripten handles this is to return the low 32 bits directly and pass
+ // the high 32 bits via the tempRet0 variable. We use Data.Long to pass the
+ // low/high pair around to methods that require a tracker.
+ var flags = 0;
+ if (tracker == null) { // Use == not === to check for both null and undefined.
+ var low = _pn_messenger_incoming_tracker(this._messenger);
+ var high = Runtime.getTempRet0();
+ tracker = new Data.Long(low, high);
+ flags = Module['Messenger'].PN_CUMULATIVE;
+ }
+
+ this._check(_pn_messenger_accept(this._messenger, tracker.getLowBitsUnsigned(), tracker.getHighBits(), flags));
+};
+
+/**
+ * Rejects the Message indicated by the tracker. If no tracker is supplied,
+ * all messages that have been returned by the get method are rejected, except
+ * those already auto-settled by passing beyond your outgoing window size.
+ * @method reject
+ * @memberof! proton.Messenger#
+ * @param {proton.Data.Long} tracker the tracker identifying the delivery.
+ */
+_Messenger_['reject'] = function(tracker) {
+ // Getting the tracker is a little tricky as it is a 64 bit number. The way
+ // emscripten handles this is to return the low 32 bits directly and pass
+ // the high 32 bits via the tempRet0 variable. We use Data.Long to pass the
+ // low/high pair around to methods that require a tracker.
+ var flags = 0;
+ if (tracker == null) { // Use == not === to check for both null and undefined.
+ var low = _pn_messenger_incoming_tracker(this._messenger);
+ var high = Runtime.getTempRet0();
+ tracker = new Data.Long(low, high);
+ flags = Module['Messenger'].PN_CUMULATIVE;
+ }
+
+ this._check(_pn_messenger_reject(this._messenger, tracker.getLowBitsUnsigned(), tracker.getHighBits(), flags));
+};
+
+/**
+ * Returns the number of messages in the outgoing message queue of a messenger.
+ * @method outgoing
+ * @memberof! proton.Messenger#
+ * @returns {number} the outgoing queue depth.
+ */
+_Messenger_['outgoing'] = function() {
+ return _pn_messenger_outgoing(this._messenger);
+};
+
+/**
+ * Returns the number of messages in the incoming message queue of a messenger.
+ * @method incoming
+ * @memberof! proton.Messenger#
+ * @returns {number} the incoming queue depth.
+ */
+_Messenger_['incoming'] = function() {
+ return _pn_messenger_incoming(this._messenger);
+};
+
+/**
+ * Adds a routing rule to a Messenger's internal routing table.
+ * <p>
+ * The route method may be used to influence how a messenger will internally treat
+ * a given address or class of addresses. Every call to the route method will
+ * result in messenger appending a routing rule to its internal routing table.
+ * <p>
+ * Whenever a message is presented to a messenger for delivery, it will match the
+ * address of this message against the set of routing rules in order. The first
+ * rule to match will be triggered, and instead of routing based on the address
+ * presented in the message, the messenger will route based on the address supplied
+ * in the rule.
+ * <p>
+ * The pattern matching syntax supports two types of matches, a '' will match any
+ * character except a '/', and a '*' will match any character including a '/'.
+ * <p>
+ * A routing address is specified as a normal AMQP address, however it may
+ * additionally use substitution variables from the pattern match that triggered
+ * the rule.
+ * <p>
+ * Any message sent to "foo" will be routed to "amqp://foo.com":
+ * <pre>
+ * route("foo", "amqp://foo.com");
+ * </pre>
+ * Any message sent to "foobar" will be routed to "amqp://foo.com/bar":
+ * <pre>
+ * route("foobar", "amqp://foo.com/bar");
+ * </pre>
+ * Any message sent to bar/<path> will be routed to the corresponding path within
+ * the amqp://bar.com domain:
+ * <pre>
+ * route("bar/*", "amqp://bar.com/$1");
+ * </pre>
+ * Supply credentials for foo.com:
+ * <pre>
+ * route("amqp://foo.com/*", "amqp://user:password@foo.com/$1");
+ * </pre>
+ * Supply credentials for all domains:
+ * <pre>
+ * route("amqp://*", "amqp://user:password@$1");
+ * </pre>
+ * Route all addresses through a single proxy while preserving the original destination:
+ * <pre>
+ * route("amqp://%/*", "amqp://user:password@proxy/$1/$2");
+ * </pre>
+ * Route any address through a single broker:
+ * <pre>
+ * route("*", "amqp://user:password@broker/$1");
+ * </pre>
+ * @method route
+ * @memberof! proton.Messenger#
+ * @param {string} pattern a glob pattern to select messages.
+ * @param {string} address an address indicating outgoing address rewrite.
+ */
+_Messenger_['route'] = function(pattern, address) {
+ var sp = Runtime.stackSave();
+ _pn_messenger_route(this._messenger,
+ allocate(intArrayFromString(pattern), 'i8', ALLOC_STACK),
+ allocate(intArrayFromString(address), 'i8', ALLOC_STACK));
+ Runtime.stackRestore(sp);
+};
+
+/**
+ * Rewrite message addresses prior to transmission.
+ * <p>
+ * Similar to route(), except that the destination of the Message is determined
+ * before the message address is rewritten.
+ * <p>
+ * The outgoing address is only rewritten after routing has been finalized. If
+ * a message has an outgoing address of "amqp://0.0.0.0:5678", and a rewriting
+ * rule that changes its outgoing address to "foo", it will still arrive at the
+ * peer that is listening on "amqp://0.0.0.0:5678", but when it arrives there,
+ * the receiver will see its outgoing address as "foo".
+ * <p>
+ * The default rewrite rule removes username and password from addresses
+ * before they are transmitted.
+ * @method rewrite
+ * @memberof! proton.Messenger#
+ * @param {string} pattern a glob pattern to select messages.
+ * @param {string} address an address indicating outgoing address rewrite.
+ */
+_Messenger_['rewrite'] = function(pattern, address) {
+ var sp = Runtime.stackSave();
+ _pn_messenger_rewrite(this._messenger,
+ allocate(intArrayFromString(pattern), 'i8', ALLOC_STACK),
+ allocate(intArrayFromString(address), 'i8', ALLOC_STACK));
+ Runtime.stackRestore(sp);
+};
+
diff --git a/proton-c/bindings/javascript/module.js b/proton-c/bindings/javascript/module.js
new file mode 100644
index 0000000..b755f59
--- /dev/null
+++ b/proton-c/bindings/javascript/module.js
@@ -0,0 +1,426 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * This file defines the Module Object which provides a namespace around the Proton
+ * Messenger API. The Module object is used extensively by the emscripten runtime,
+ * however for convenience it is exported with the name "proton" and not "Module".
+ * <p>
+ * The emscripten compiled proton-c code and the JavaScript binding code will be
+ * minified by the Closure compiler, so all comments will be stripped from the
+ * actual library.
+ * <p>
+ * This JavaScript wrapper provides a somewhat more idiomatic object oriented
+ * interface which abstracts the low-level emscripten based implementation details
+ * from client code. Any similarities to the Proton Python binding are deliberate.
+ * @file
+ */
+
+/**
+ * The Module Object is exported by emscripten for all execution platforms, we
+ * use it as a namespace to allow us to selectively export only what we wish to
+ * be publicly visible from this package/module, which is wrapped in a closure.
+ * <p>
+ * Internally the binding code uses the associative array form for declaring
+ * exported properties to prevent the Closure compiler from minifying e.g.
+ * <pre>Module['Messenger'] = ...</pre>
+ * Exported Objects can however be used in client code using a more convenient
+ * and obvious proton namespace, e.g.:
+ * <pre>
+ * var proton = require('qpid-proton');
+ * var messenger = new proton.Messenger();
+ * var message = new proton.Message();
+ * ...
+ * </pre>
+ * The core part of this library is actually proton-c compiled into JavaScript.
+ * In order to provide C style memory management (malloc/free) emscripten uses
+ * a "virtual heap", which is actually a pre-allocated ArrayBuffer. The size of
+ * this virtual heap is set as part of the runtime initialisation and cannot be
+ * changed subsequently (the default size is 16*1024*1024 = 16777216).
+ * <p>
+ * Applications can specify the size of virtual heap that they require via the
+ * global variable PROTON_TOTAL_MEMORY, this must be set <b>before</b> the library is
+ * loaded e.g. in Node.js an application would do:
+ * <pre>
+ * PROTON_TOTAL_MEMORY = 50000000; // Note no var - it needs to be global.
+ * var proton = require('qpid-proton');
+ * ...
+ * </pre>
+ * A browser based application would do:
+ * <pre>
+ * <script type="text/javascript">PROTON_TOTAL_MEMORY = 50000000;</script>
+ * <script type="text/javascript" src="proton.js"></script>
+ * </pre>
+ * If the global variable PROTON_TOTAL_MEMORY has been set by the application this
+ * will result in the emscripten heap getting set to the next multiple of
+ * 16777216 above PROTON_TOTAL_MEMORY.
+ * <p>
+ * The global variable PROTON_TOTAL_STACK may be used in a similar way to increase
+ * the stack size from its default of 5*1024*1024 = 5242880. It is worth noting
+ * that Strings are allocated on the stack, so you may need this if you end up
+ * wanting to send very large strings.
+ * @namespace proton
+ */
+var Module = {};
+
+if (typeof global === 'object') { // If Node.js
+ if (global['PROTON_TOTAL_MEMORY']) {
+ Module['TOTAL_MEMORY'] = global['PROTON_TOTAL_MEMORY'];
+ }
+ if (global['PROTON_TOTAL_STACK']) {
+ Module['TOTAL_STACK'] = global['PROTON_TOTAL_STACK'];
+ }
+} else if (typeof window === 'object') { // If browser
+ if (window['PROTON_TOTAL_MEMORY']) {
+ Module['TOTAL_MEMORY'] = window['PROTON_TOTAL_MEMORY'];
+ }
+ if (window['PROTON_TOTAL_STACK']) {
+ Module['TOTAL_STACK'] = window['PROTON_TOTAL_STACK'];
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* EventDispatch */
+/* */
+/*****************************************************************************/
+
+/**
+ * EventDispatch is a Singleton class that allows callbacks to be registered,
+ * which will get triggered by the emscripten WebSocket network callbacks.
+ * Clients of Messenger will register callbacks by calling:
+ * <pre>
+ * messenger.on('error', <callback function>);
+ * messenger.on('work', <callback function>);
+ * messenger.on('subscription', <callback function>);
+ * </pre>
+ * EventDispatch supports callback registration from multiple Messenger instances.
+ * The client callbacks will actually be called when a given messenger has work
+ * available or a WebSocket close has been occurred.
+ * <p>
+ * The approach implemented here allows the registered callbacks to follow a
+ * similar pattern to _process_incoming and _process_outgoing in async.py
+ * @constructor proton.EventDispatch
+ */
+Module.EventDispatch = new function() { // Note the use of new to create a Singleton.
+ var POLLIN = 0x001;
+ var POLLOUT = 0x004;
+ var _error = null;
+ var _messengers = {}; // Keyed by name.
+ var _selectables = {}; // Keyed by file descriptor.
+
+ var _initialise = function() {
+ /**
+ * Initialises the emscripten network callback functions. This needs
+ * to be done the first time we call registerMessenger rather than
+ * when we create the Singleton because emscripten's socket filesystem
+ * has to be mounted before can listen for any of these events.
+ */
+ Module['websocket']['on']('open', _pump);
+ Module['websocket']['on']('connection', _pump);
+ Module['websocket']['on']('message', _pump);
+ Module['websocket']['on']('close', _closeHandler);
+ Module['websocket']['on']('error', _errorHandler);
+
+ /**
+ * For Node.js the network code uses the ws WebSocket library, see
+ * https://github.com/einaros/ws. The following is a "Monkey Patch"
+ * that fixes a problem with Receiver.js where it wasn't checking if
+ * an Object was null before accessing its properties, so it was
+ * possible to see errors like:
+ * TypeError: Cannot read property 'fragmentedOperation' of null
+ * at Receiver.endPacket (.....node_modules/ws/lib/Receiver.js:224:18)
+ * This problem is generally seen in Server code after messenger.stop()
+ * I *think* that the underlying issue is actually because ws calls
+ * cleanup directly rather than pushing it onto the event loop so the
+ * this.state stuff gets cleared before the endPacket method is called.
+ * This fix simply interposes a check to avoid calling endPacket if
+ * the state has been cleared (i.e. the WebSocket has been closed).
+ */
+ if (ENVIRONMENT_IS_NODE) {
+ try {
+ var ws = require('ws');
+ // Array notation to stop Closure compiler minifying properties we need.
+ ws['Receiver'].prototype['originalEndPacket'] = ws['Receiver'].prototype['endPacket'];
+ ws['Receiver'].prototype['endPacket'] = function() {
+ if (this['state']) {
+ this['originalEndPacket']();
+ }
+ };
+ } catch (e) {
+ console.error("Failed to apply Monkey Patch to ws WebSocket library");
+ }
+ }
+
+ _initialise = function() {}; // After first call replace with null function.
+ };
+
+ /**
+ * Messenger error handling can be a bit inconsistent and in several places
+ * rather than returning an error code or setting an error it simply writes
+ * to fprintf. This is something of a Monkey Patch that replaces the emscripten
+ * library fprintf call with one that checks the message and sets a variable
+ * if the message is an ERROR. TODO At some point hopefully Dominic Evans'
+ * patch on Jira PROTON-571 will render this code redundant.
+ */
+ _fprintf = function(stream, format, varargs) {
+ var array = __formatString(format, varargs);
+ array.pop(); // Remove the trailing \n
+ var string = intArrayToString(array); // Convert to native JavaScript string.
+ if (string.indexOf('ERROR') === -1) { // If not an ERROR just log the message.
+ console.log(string);
+ } else {
+ _error = string;
+ }
+ };
+
+ /**
+ * This method iterates through all registered Messengers and retrieves any
+ * pending selectables, which are stored in a _selectables map keyed by fd.
+ */
+ var _updateSelectables = function() {
+ var sel = 0;
+ var fd = -1;
+ for (var name in _messengers) {
+ var messenger = _messengers[name];
+ while ((sel = _pn_messenger_selectable(messenger._messenger))) {
+ fd = _pn_selectable_fd(sel);
+ // Only register valid selectables, otherwise free them.
+ if (fd === -1) {
+ _pn_selectable_free(sel);
+ } else {
+ _selectables[fd] = {messenger: messenger, selectable: sel};
+ }
+ }
+ }
+ return fd; // Return the most recently added selector's file descriptor.
+ };
+
+ /**
+ * Continually pump data while there's still work to do.
+ */
+ var _pump = function() {
+ while (_pumpOnce());
+ };
+
+ /**
+ * This method more or less follows the pattern of the pump_once method from
+ * class Pump in tests/python/proton_tests/messenger.py. It looks a little
+ * different because the select/poll implemented here uses some low-level
+ * emscripten internals (stream = FS.getStream(fd), sock = stream.node.sock,
+ * mask = sock.sock_ops.poll(sock)). We use the internals so we don't have
+ * to massage from file descriptors into the C style poll interface.
+ */
+ var _pumpOnce = function() {
+ _updateSelectables();
+
+ var count = 0;
+ for (var fd in _selectables) {
+ var selectable = _selectables[fd];
+ var sel = selectable.selectable;
+ var terminal = _pn_selectable_is_terminal(sel);
+ if (terminal) {
+ _pn_selectable_free(sel);
+ delete _selectables[fd];
+ } else {
+ var stream = FS.getStream(fd);
+ if (stream) {
+ var sock = stream.node.sock;
+ if (sock.sock_ops.poll) {
+ var mask = sock.sock_ops.poll(sock); // Low-level poll call.
+ if (mask) {
+ var messenger = selectable.messenger;
+ var capacity = _pn_selectable_capacity(sel) > 0;
+ var pending = _pn_selectable_pending(sel) > 0;
+
+ if ((mask & POLLIN) && capacity) {
+//console.log("- readable fd = " + fd + ", capacity = " + _pn_selectable_capacity(sel));
+ _error = null; // May get set by _pn_selectable_readable.
+ _pn_selectable_readable(sel);
+ count++; // Should this be inside the test for _error? Don't know.
+ var errno = messenger['getErrno']();
+ _error = errno ? messenger['getError']() : _error;
+ if (_error) {
+ _errorHandler([fd, 0, _error]);
+ } else {
+ // Don't send work event if it's a listen socket.
+ if (!sock.server) {
+ messenger._checkSubscriptions();
+ messenger._emit('work');
+ }
+ }
+ }
+ if ((mask & POLLOUT) && pending) {
+//console.log("- writeable fd = " + fd + ", pending = " + _pn_selectable_pending(sel));
+ _pn_selectable_writable(sel);
+ //TODO looks like this block isn't needed. Need to
+ //check with a test-case that writes data as fast as
+ //it can. If not needed then delete.
+ /*
+ count++;
+ // Check _selectables again in case the call to
+ // _pn_selectable_writable caused a socket close.
+ if (_selectables[fd]) {
+ messenger._checkSubscriptions();
+ messenger._emit('work');
+ }
+ */
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return count;
+ };
+
+ /**
+ * Handler for the emscripten socket close event.
+ */
+ var _closeHandler = function(fd) {
+ var selectable = _selectables[fd];
+ if (selectable) {
+ // Close and remove the selectable.
+ var sel = selectable.selectable;
+ _pn_selectable_free(sel); // This closes the underlying socket too.
+ delete _selectables[fd];
+
+ var messenger = selectable.messenger;
+ messenger._emit('work');
+ }
+ };
+
+ /**
+ * Handler for the emscripten socket error event.
+ */
+ var _errorHandler = function(error) {
+ var fd = error[0];
+ var message = error[2];
+
+ _updateSelectables();
+
+ var selectable = _selectables[fd];
+ if (selectable) {
+ // Close and remove the selectable.
+ var sel = selectable.selectable;
+ _pn_selectable_free(sel); // This closes the underlying socket too.
+ delete _selectables[fd];
+
+ var messenger = selectable.messenger;
+
+ // Remove any pending Subscriptions whose fd matches the error fd.
+ var subscriptions = messenger._pendingSubscriptions;
+ for (var i = 0; i < subscriptions.length; i++) {
+ subscription = subscriptions[i];
+ // Use == not === as we don't care if fd is a number or a string.
+ if (subscription.fd == fd) {
+ messenger._pendingSubscriptions.splice(i, 1);
+ if (message.indexOf('EHOSTUNREACH:') === 0) {
+ message = 'CONNECTION ERROR (' + subscription.source + '): bind: Address already in use';
+ }
+ messenger._emit('error', new Module['SubscriptionError'](subscription.source, message));
+ return;
+ }
+ }
+
+ messenger._emit('error', new Module['MessengerError'](message));
+ }
+ };
+
+ /**
+ * Flush any data that has been written by the Messenger put() method.
+ * @method flush
+ */
+ this.flush = function() {
+ _pump();
+ };
+
+ /**
+ * Subscribe to a specified source address.
+ * <p>
+ * This method is delegated to by the subscribe method of {@link proton.Messenger}.
+ * We delegate to EventDispatch because we create Subscription objects that
+ * contain some additional information (such as file descriptors) which are
+ * only available to EventDispatch and we don't really want to expose to the
+ * wider API. This low-level information is mainly used for error handling
+ * which is itself encapsulated in EventDispatch.
+ * @method subscribe
+ * @memberof! proton.EventDispatch#
+ * @param {proton.Messenger} messenger the Messenger instance that this
+ * subscription relates to.
+ * @param {string} source the address that we'd like to subscribe to.
+ */
+ this.subscribe = function(messenger, source) {
+ // First update selectables before subscribing so we can work out the
+ // Subscription fd (which will be the listen file descriptor).
+ _updateSelectables();
+ var sp = Runtime.stackSave();
+ var subscription = _pn_messenger_subscribe(messenger._messenger,
+ allocate(intArrayFromString(source), 'i8', ALLOC_STACK));
+ Runtime.stackRestore(sp);
+ var fd = _updateSelectables();
+
+ subscription = new Subscription(subscription, source, fd);
+ messenger._pendingSubscriptions.push(subscription);
+
+ // For passive subscriptions emit a subscription event (almost) immediately,
+ // otherwise defer until the address has been resolved remotely.
+ if (subscription.passive) {
+ // We briefly delay the call to checkSubscriptions because it is possible
+ // for passive subscriptions to fail if another process is bound to the
+ // port specified in the subscription.
+ var check = function() {messenger._checkSubscriptions();};
+ setTimeout(check, 10);
+ }
+
+ return subscription;
+ };
+
+ /**
+ * Register the specified Messenger as being interested in network events.
+ * @method registerMessenger
+ * @memberof! proton.EventDispatch#
+ * @param {proton.Messenger} messenger the Messenger instance we want to
+ * register to receive network events.
+ */
+ this.registerMessenger = function(messenger) {
+ _initialise();
+
+ var name = messenger['getName']();
+ _messengers[name] = messenger;
+
+ // Set the Messenger "passive" as we are supplying our own event loop here.
+ _pn_messenger_set_passive(messenger._messenger, true);
+ };
+
+ /**
+ * Unregister the specified Messenger from interest in network events.
+ * @method unregisterMessenger
+ * @memberof! proton.EventDispatch#
+ * @param {proton.Messenger} messenger the Messenger instance we want to
+ * unregister from receiving network events.
+ */
+ this.unregisterMessenger = function(messenger) {
+ var name = messenger['getName']();
+ delete _messengers[name];
+ };
+};
+
diff --git a/proton-c/bindings/javascript/qpid-proton/LICENSE b/proton-c/bindings/javascript/qpid-proton/LICENSE
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/proton-c/bindings/javascript/qpid-proton/LICENSE
@@ -0,0 +1,203 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/proton-c/bindings/javascript/qpid-proton/README.md b/proton-c/bindings/javascript/qpid-proton/README.md
new file mode 100644
index 0000000..db026c7
--- /dev/null
+++ b/proton-c/bindings/javascript/qpid-proton/README.md
@@ -0,0 +1,4 @@
+qpid-proton
+-----------
+
+apache qpid proton messenger AMQP 1.0 library
diff --git a/proton-c/bindings/javascript/qpid-proton/lib/.gitignore b/proton-c/bindings/javascript/qpid-proton/lib/.gitignore
new file mode 100644
index 0000000..2b1f291
--- /dev/null
+++ b/proton-c/bindings/javascript/qpid-proton/lib/.gitignore
@@ -0,0 +1,21 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Currently empty .gitignore
+# Used to ensure git creates the otherwise empty directory
diff --git a/proton-c/bindings/javascript/qpid-proton/package.json b/proton-c/bindings/javascript/qpid-proton/package.json
new file mode 100644
index 0000000..ffc5ebe
--- /dev/null
+++ b/proton-c/bindings/javascript/qpid-proton/package.json
@@ -0,0 +1,11 @@
+{
+ "name" : "qpid-proton",
+ "version": "0.7.0",
+ "description": "apache qpid proton messenger AMQP 1.0 library",
+ "repository": {
+ "type": "svn",
+ "url": "https://svn.apache.org/repos/asf/qpid/proton/trunk/"
+ },
+ "main" : "./lib/proton.js",
+ "dependencies": {}
+}
diff --git a/proton-c/bindings/javascript/subscription.js b/proton-c/bindings/javascript/subscription.js
new file mode 100644
index 0000000..89fd1a3
--- /dev/null
+++ b/proton-c/bindings/javascript/subscription.js
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*****************************************************************************/
+/* */
+/* Subscription */
+/* */
+/*****************************************************************************/
+
+/**
+ * Constructs a Subscription instance.
+ * @classdesc This class is a wrapper for Messenger's subscriptions.
+ * Subscriptions should never be *directly* instantiated by client code only via
+ * Messenger.subscribe() or Messenger.incomingSubscription(), so we declare the
+ * constructor in the scope of the package and don't export it via Module.
+ * @constructor Subscription
+ * @param {number} subscription a pointer to the underlying subscription object.
+ * @param {string} source the address that we want to subscribe to.
+ * @param {number} fd the file descriptor associated with the subscription. This
+ * is used internally to tidy up during error handling.
+ */
+var Subscription = function(subscription, source, fd) { // Subscription Constructor.
+ this._subscription = subscription;
+ this.source = source;
+ this.fd = fd;
+ if (source.indexOf('~') !== -1) {
+ this.passive = true;
+ } else {
+ this.passive = false;
+ }
+};
+
+/**
+ * TODO Not sure exactly what pn_subscription_get_context does.
+ * @method getContext
+ * @memberof! Subscription#
+ * @returns the Subscription's Context.
+ */
+Subscription.prototype['getContext'] = function() {
+ return _pn_subscription_get_context(this._subscription);
+};
+
+/**
+ * TODO Not sure exactly what pn_subscription_set_context does.
+ * @method setContext
+ * @memberof! Subscription#
+ * @param context the Subscription's new Context.
+ */
+Subscription.prototype['setContext'] = function(context) {
+ _pn_subscription_set_context(this._subscription, context);
+};
+
+/**
+ * @method getAddress
+ * @memberof! Subscription#
+ * @returns the Subscription's Address.
+ */
+Subscription.prototype['getAddress'] = function() {
+ if (this.passive) {
+ return this.source;
+ }
+ return Pointer_stringify(_pn_subscription_address(this._subscription));
+};
+
diff --git a/tests/javascript/codec.js b/tests/javascript/codec.js
new file mode 100644
index 0000000..ce4890d
--- /dev/null
+++ b/tests/javascript/codec.js
@@ -0,0 +1,569 @@
+#!/usr/bin/env node
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * This is a fairly literal JavaScript port of codec.py used to unit test the
+ * proton.Data wrapper class. This suite of tests is actually testing the low
+ * level implementation methods used to access the AMQP type system and in
+ * practice most normal users wouldn't need to call these directly, rather users
+ * should simply use the putObject() and getObject() methods.
+ */
+
+// Check if the environment is Node.js and if not log an error and exit.
+if (typeof process === 'object' && typeof require === 'function') {
+ var unittest = require("./unittest.js");
+ var assert = require("assert");
+
+ // Increase the virtual heap available to the emscripten compiled C runtime.
+ // This allows us to test a really big string.
+ PROTON_TOTAL_MEMORY = 140000000;
+ PROTON_TOTAL_STACK = 25000000; // Needs to be bigger than the biggest string.
+ var proton = require("qpid-proton");
+
+ // Extend TestCase by creating a prototype instance and adding test methods as properties.
+ var DataTest = new unittest.TestCase();
+
+ DataTest.setUp = function() {
+ this.data = new proton.Data();
+ };
+
+ DataTest.tearDown = function() {
+ this.data.free();
+ this.data = null;
+ };
+
+ DataTest.testTopLevelNext = function() {
+ console.log("testTopLevelNext");
+ assert(this.data.next() === null);
+ this.data.putNULL();
+ this.data.putBOOL(false);
+ this.data.putINT(0);
+ assert(this.data.next() === null);
+ this.data.rewind();
+ assert(this.data.next() === proton.Data.NULL);
+ assert(this.data.next() === proton.Data.BOOL);
+ assert(this.data.next() === proton.Data.INT);
+ assert(this.data.next() === null);
+ console.log("OK\n");
+ };
+
+ DataTest.testNestedNext = function() {
+ console.log("testNestedNext");
+ assert(this.data.next() === null);
+ this.data.putNULL();
+ assert(this.data.next() === null);
+ this.data.putLISTNODE();
+ assert(this.data.next() === null);
+ this.data.putBOOL(false);
+ assert(this.data.next() === null);
+ this.data.rewind();
+ assert(this.data.next() === proton.Data.NULL);
+ assert(this.data.next() === proton.Data.LIST);
+ this.data.enter();
+ assert(this.data.next() === null);
+ this.data.putUBYTE(0);
+ assert(this.data.next() === null);
+ this.data.putUINT(0);
+ assert(this.data.next() === null);
+ this.data.putINT(0);
+ assert(this.data.next() === null);
+ this.data.exit();
+ assert(this.data.next() === proton.Data.BOOL);
+ assert(this.data.next() === null);
+
+ this.data.rewind();
+ assert(this.data.next() === proton.Data.NULL);
+ assert(this.data.next() === proton.Data.LIST);
+ assert(this.data.enter());
+ assert(this.data.next() === proton.Data.UBYTE);
+ assert(this.data.next() === proton.Data.UINT);
+ assert(this.data.next() === proton.Data.INT);
+ assert(this.data.next() === null);
+ assert(this.data.exit());
+ assert(this.data.next() === proton.Data.BOOL);
+ assert(this.data.next() === null);
+ console.log("OK\n");
+ };
+
+ DataTest.testEnterExit = function() {
+ console.log("testEnterExit");
+ assert(this.data.next() === null);
+ assert(!this.data.enter());
+ this.data.putLISTNODE();
+ assert(this.data.enter());
+ assert(this.data.next() === null);
+ this.data.putLISTNODE();
+ assert(this.data.enter());
+ this.data.putLISTNODE();
+ assert(this.data.enter());
+ assert(this.data.exit());
+ assert(this.data.getLISTNODE() === 0);
+ assert(this.data.exit());
+ assert(this.data.getLISTNODE() === 1);
+ assert(this.data.exit());
+ assert(this.data.getLISTNODE() === 1);
+ assert(!this.data.exit());
+ assert(this.data.getLISTNODE() === 1);
+ assert(this.data.next() === null);
+
+ this.data.rewind();
+ assert(this.data.next() === proton.Data.LIST);
+ assert(this.data.getLISTNODE() === 1);
+ assert(this.data.enter());
+ assert(this.data.next() === proton.Data.LIST);
+ assert(this.data.getLISTNODE() === 1);
+ assert(this.data.enter());
+ assert(this.data.next() === proton.Data.LIST);
+ assert(this.data.getLISTNODE() === 0);
+ assert(this.data.enter());
+ assert(this.data.next() === null);
+ assert(this.data.exit());
+ assert(this.data.getLISTNODE() === 0);
+ assert(this.data.exit());
+ assert(this.data.getLISTNODE() === 1);
+ assert(this.data.exit());
+ assert(this.data.getLISTNODE() === 1);
+ assert(!this.data.exit());
+ console.log("OK\n");
+ };
+
+ /**
+ * This tests the "low level" putARRAYNODE/getARRAYNODE methods.
+ * In general though applications would create a proton.Data.Array and use the
+ * higher level putARRAY/getARRAY
+ */
+ DataTest._testArray = function(dtype, descriptor, atype, values) {
+ var values = Array.prototype.slice.apply(arguments, [3]);
+ dtype = (dtype == null) ? null : dtype.toUpperCase();
+ atype = atype.toUpperCase();
+
+ // Create an array node, enter it and put the descriptor (if present) and values.
+ this.data.putARRAYNODE(dtype != null, proton.Data[atype]);
+ this.data.enter();
+ if (dtype != null) {
+ var putter = 'put' + dtype;
+ this.data[putter](descriptor);
+ }
+ var putter = 'put' + atype;
+ for (var i = 0; i < values.length; i++) {
+ this.data[putter](values[i]);
+ }
+ this.data.exit();
+
+ // Check that we did indeed add an Array node
+ this.data.rewind();
+ assert(this.data.next() === proton.Data.ARRAY);
+
+ // Get the count, described and type metadata from the array node and compare
+ // with the values we passed to putARRAYNODE.
+ var metadata = this.data.getARRAYNODE();
+ var count = metadata.count;
+ var described = metadata.described;
+ var type = metadata.type;
+
+ assert(count === values.length);
+ if (dtype == null) {
+ assert(described === false);
+ } else {
+ assert(described === true);
+ }
+ assert(type === proton.Data[atype]);
+
+ // Enter the array node and compare the descriptor and values with those that
+ // we put into the array.
+ assert(this.data.enter());
+ if (described) {
+ assert(this.data.next() === proton.Data[dtype]);
+ var getter = 'get' + dtype;
+ var gotten = this.data[getter]();
+ assert(gotten.toString() === descriptor.toString());
+ }
+ var getter = 'get' + atype;
+ for (var i = 0; i < values.length; i++) {
+ assert(this.data.next() === proton.Data[atype]);
+ var gotten = this.data[getter]();
+ assert(gotten.toString() === values[i].toString());
+ }
+ assert(this.data.next() === null);
+ assert(this.data.exit());
+ };
+
+ DataTest.testStringArray = function() {
+ console.log("testStringArray");
+ this._testArray(null, null, "string", "one", "two", "three");
+
+ // Now try using the proton.Data.Array class.
+ this.data.clear();
+ var put = new proton.Data.Array("STRING", ["four", "five", "six"]);
+ this.data.putARRAY(put);
+ var get = this.data.getARRAY();
+ assert(get.equals(put));
+ console.log("OK\n");
+ };
+
+ DataTest.testDescribedStringArray = function() {
+ console.log("testDescribedStringArray");
+ this._testArray("symbol", "url", "string", "one", "two", "three");
+
+ // Now try using the proton.Data.Array class.
+ this.data.clear();
+ var put = new proton.Data.Array("STRING", ["four", "five", "six"], new proton.Data.Symbol("url"));
+ this.data.putARRAY(put);
+ var get = this.data.getARRAY();
+ assert(get.equals(put));
+ console.log("OK\n");
+ };
+
+ DataTest.testIntArray = function() {
+ console.log("testIntArray");
+ this._testArray(null, null, "int", 1, 2, 3);
+
+ // Now try using the proton.Data.Array class.
+ this.data.clear();
+ var put = new proton.Data.Array("INT", [4, 5, 6]);
+ this.data.putARRAY(put);
+ var get = this.data.getARRAY();
+ assert(get.equals(put));
+ console.log("OK\n");
+ };
+
+ DataTest.testUUIDArray = function() {
+ console.log("testUUIDArray");
+ this._testArray(null, null, "uuid", new proton.Data.Uuid(), new proton.Data.Uuid(), new proton.Data.Uuid());
+
+ // Now try using the proton.Data.Array class.
+ this.data.clear();
+ var put = new proton.Data.Array("UUID", [new proton.Data.Uuid(), new proton.Data.Uuid(), new proton.Data.Uuid()]);
+ this.data.putARRAY(put);
+ var get = this.data.getARRAY();
+ assert(get.equals(put));
+ console.log("OK\n");
+ };
+
+ DataTest.testEmptyArray = function() {
+ console.log("testEmptyArray");
+ this._testArray(null, null, "null");
+
+ // Now try using the proton.Data.Array class.
+ this.data.clear();
+ var put = new proton.Data.Array();
+ this.data.putARRAY(put);
+ var get = this.data.getARRAY();
+ assert(get.equals(put));
+ console.log("OK\n");
+ };
+
+ DataTest.testDescribedEmptyArray = function() {
+ console.log("testDescribedEmptyArray");
+ this._testArray("long", 0, "null");
+
+ // Now try using the proton.Data.Array class.
+ this.data.clear();
+ var put = new proton.Data.Array((0).long());
+ this.data.putARRAY(put);
+ var get = this.data.getARRAY();
+ assert(get.equals(put));
+ console.log("OK\n");
+ };
+
+ DataTest._test = function(dtype, values) {
+ var values = Array.prototype.slice.apply(arguments, [1]);
+ var lastValue = values[values.length - 1];
+
+ // Default equality function. Note that we use valueOf because some of the
+ // types we are trying to compare (Symbol, Timestamp, Uuid etc.) are object
+ // types and we want to compare their value not whether they are the same object.
+ var eq = function(x, y) {return x.valueOf() === y.valueOf();};
+
+ if (typeof lastValue === 'function') {
+ eq = values.pop();
+ }
+
+ dtype = dtype.toUpperCase();
+ var ntype = proton.Data[dtype];
+ var putter = 'put' + dtype;
+ var getter = 'get' + dtype;
+
+ for (var i = 0; i < values.length; i++) {
+ var v = values[i];
+ /*
+ * Replace the array element with its value. We do this to make testing
+ * simpler for Binary elements. In the case of Binary putBINARY "consumes"
+ * the data, in other words ownership of the underlying raw data transfers
+ * to the Data object so the v object becomes "empty" after calling the
+ * putter. Calling its valueOf() happens to call its toString() which
+ * provides a stringified version of the Binary whilst also working for
+ * the other data types we want to test too.
+ */
+ values[i] = v.valueOf();
+ this.data[putter](v);
+ var gotten = this.data[getter]();
+ assert(eq(gotten, values[i]));
+ }
+
+ this.data.rewind();
+
+ for (var i = 0; i < values.length; i++) {
+ var v = values[i];
+ var vtype = this.data.next();
+ assert(vtype === ntype);
+ var gotten = this.data[getter]();
+ assert(eq(gotten, v));
+ }
+
+ // Test encode and decode methods.
+ var encoded = this.data.encode();
+ var copy = new proton.Data();
+ while (encoded) {
+ encoded = copy.decode(encoded);
+ }
+ copy.rewind();
+
+ for (var i = 0; i < values.length; i++) {
+ var v = values[i];
+ var vtype = copy.next();
+ assert(vtype === ntype);
+ var gotten = copy[getter]();
+ assert(eq(gotten, v));
+ }
+ copy.free();
+ };
+
+ DataTest.testInt = function() {
+ console.log("testInt");
+ this._test("int", 1, 2, 3, -1, -2, -3);
+ console.log("OK\n");
+
+ };
+
+ DataTest.testString = function() {
+ console.log("testString");
+ this._test("string", "one", "two", "three", "this is a test", "");
+ console.log("OK\n");
+ };
+
+ DataTest.testBigString = function() {
+ // Try a 2MB string, this is about as big as we can cope with using the default
+ // emscripten heap size.
+ console.log("testBigString");
+ var data = "";
+ for (var i = 0; i < 2000000; i++) {
+ data += "*";
+ }
+ var string = "start\n" + data + "\nfinish\n";
+ this._test("string", string);
+ console.log("OK\n");
+ };
+
+/* TODO - currently emscripten isn't respecting Module['TOTAL_STACK'] so setting PROTON_TOTAL_STACK doesn't actually increase the stack size.
+ DataTest.testReallyBigString = function() {
+ // Try a 20MB string, this needs a bigger emscripten heap size and more stack
+ // as the default stack is 5MB and strings are allocated on the stack.
+ console.log("testReallyBigString");
+ var data = "";
+ for (var i = 0; i < 20000000; i++) {
+ data += "*";
+ }
+ var string = "start\n" + data + "\nfinish\n";
+ this._test("string", string);
+ console.log("OK\n");
+ };
+*/
+
+ DataTest.testFloat = function() {
+ console.log("testFloat");
+ // We have to use a special comparison here because JavaScript internally
+ // only uses doubles and converting between floats and doubles is imprecise.
+ this._test("float", 0, 1, 2, 3, 0.1, 0.2, 0.3, -1, -2, -3, -0.1, -0.2, -0.3,
+ function(x, y) {return (x - y < 0.000001);});
+ console.log("OK\n");
+ };
+
+ DataTest.testDouble = function() {
+ console.log("testDouble");
+ this._test("double", 0, 1, 2, 3, 0.1, 0.2, 0.3, -1, -2, -3, -0.1, -0.2, -0.3);
+ console.log("OK\n");
+ };
+
+ DataTest.testBinary = function() {
+ console.log("testBinary");
+ this._test("binary", new proton.Data.Binary(["t".char(), "h".char(), "i".char(), "s".char()]),
+ new proton.Data.Binary("is"), new proton.Data.Binary("a"), new proton.Data.Binary("test"),
+ new proton.Data.Binary("of"), new proton.Data.Binary("двоичные данные"));
+ console.log("OK\n");
+ };
+
+ DataTest.testSymbol = function() {
+ console.log("testSymbol");
+ this._test("symbol", new proton.Data.Symbol("this is a symbol test"),
+ new proton.Data.Symbol("bleh"), new proton.Data.Symbol("blah"));
+ console.log("OK\n");
+ };
+
+ DataTest.testTimestamp = function() {
+ console.log("testTimestamp");
+ this._test("timestamp", new Date(0), new Date(12345), new Date(1000000));
+ console.log("OK\n");
+ };
+
+ DataTest.testChar = function() {
+ console.log("testChar");
+ this._test("char", 'a', 'b', 'c', '\u1234');
+ console.log("OK\n");
+ };
+
+ DataTest.testUUID = function() {
+ console.log("testUUID");
+ this._test("uuid", new proton.Data.Uuid(), new proton.Data.Uuid(), new proton.Data.Uuid());
+ console.log("OK\n");
+ };
+
+ /* TODO
+ DataTest.testDecimal32 = function() {
+ console.log("testDecimal32");
+ //this._test("decimal32", 0, 1, 2, 3, 4, Math.pow(2, 30));
+ };
+
+ DataTest.testDecimal64 = function() {
+ console.log("testDecimal64");
+ //this._test("decimal64", 0, 1, 2, 3, 4, Math.pow(2, 60));
+ };
+
+ DataTest.testDecimal128 = function() {
+ console.log("testDecimal128");
+ // TODO
+ };
+ */
+
+ DataTest.testCopy = function() {
+ console.log("testCopy");
+ this.data.putDESCRIBEDNODE();
+ this.data.enter();
+ this.data.putULONG(123);
+ this.data.putMAPNODE();
+ this.data.enter();
+ this.data.putSTRING("pi");
+ this.data.putDOUBLE(3.14159265359);
+ this.data.exit();
+ this.data.exit();
+
+ var dst = this.data.copy();
+ var copy = dst.format();
+ var orig = this.data.format();
+ assert(copy === orig);
+ dst.free();
+ console.log("OK\n");
+ };
+
+ DataTest.testCopyNested = function() {
+ console.log("testCopyNested");
+ var nested = [1, 2, 3, [4, 5, 6], 7, 8, 9];
+ this.data.putObject(nested);
+ var dst = this.data.copy();
+ assert(dst.format() === this.data.format());
+ dst.free();
+ console.log("OK\n");
+ };
+
+ DataTest.testCopyNestedArray = function() {
+ console.log("testCopyNestedArray");
+ var nested = [new proton.Data.Array("LIST", [
+ ["first", [new proton.Data.Array("INT", [1, 2, 3]), "this"]],
+ ["second", [new proton.Data.Array("INT", [1, 2, 3]), "is"]],
+ ["third", [new proton.Data.Array("INT", [1, 2, 3]), "fun"]]
+ ]),
+ "end"];
+ this.data.putObject(nested);
+ var dst = this.data.copy();
+ assert(dst.format() === this.data.format());
+ dst.free();
+ console.log("OK\n");
+ };
+
+ DataTest.testRoundTrip = function() {
+ console.log("testRoundTrip");
+ var obj = {key: new Date(1234),
+ 123: "blah",
+ c: "bleh",
+ desc: new proton.Data.Described("http://example.org", new proton.Data.Symbol("url")),
+ array: new proton.Data.Array("INT", [1, 2, 3]),
+ list: [1, 2, 3, null, 4],
+ boolean: true};
+ // Serialise obj into this.data.
+ this.data.putObject(obj);
+
+ // Encode this.data into a Binary representation.
+ var enc = this.data.encode();
+
+ // Create a new Data instance and decode from the Binary representation
+ // consuming the Binary contents in the process.
+ var data = new proton.Data();
+ data.decode(enc);
+
+ assert(data.format() === this.data.format());
+
+ // Deserialise from the copied Data instance into a new JavaScript Object.
+ data.rewind();
+ assert(data.next());
+ var copy = data.getObject();
+
+ // Create yet another Data instance and serialise the new Object into that.
+ var data1 = new proton.Data();
+ data1.putObject(copy);
+
+ // Compare the round tripped Data with the original one.
+ assert(data1.format() === this.data.format());
+
+ data.free();
+ data1.free();
+ console.log("OK\n");
+ };
+
+ DataTest.testLookup = function() {
+ console.log("testLookup");
+ var obj = {key: "value",
+ pi: 3.14159,
+ list: [1, 2, 3, 4]};
+ // Serialise obj into this.data.
+ this.data.putObject(obj);
+ this.data.rewind();
+ this.data.next();
+ this.data.enter();
+ this.data.narrow();
+ assert(this.data.lookup("pi"));
+ assert(this.data.getObject() === 3.14159);
+ this.data.rewind();
+ assert(this.data.lookup("key"));
+ assert(this.data.getObject() === "value");
+ this.data.rewind();
+ assert(this.data.lookup("list"));
+ assert(this.data.getObject().toString() === "1,2,3,4");
+ this.data.widen();
+ this.data.rewind();
+ assert(!this.data.lookup("pi"));
+ console.log("OK\n");
+ };
+
+ DataTest.run();
+} else {
+ console.error("codec.js should be run in Node.js");
+}
+
diff --git a/tests/javascript/message.js b/tests/javascript/message.js
new file mode 100644
index 0000000..dca6f68
--- /dev/null
+++ b/tests/javascript/message.js
@@ -0,0 +1,359 @@
+#!/usr/bin/env node
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * This is a fairly literal JavaScript port of message.py used to unit test the
+ * proton.Message wrapper class.
+ */
+
+// Check if the environment is Node.js and if not log an error and exit.
+if (typeof process === 'object' && typeof require === 'function') {
+ var unittest = require("./unittest.js");
+ var assert = require("assert");
+ var proton = require("qpid-proton");
+
+ /**
+ * JavaScript Implementation of Python's range() function taken from:
+ * http://stackoverflow.com/questions/8273047/javascript-function-similar-to-python-range
+ */
+ var range = function(start, stop, step) {
+ if (typeof stop == 'undefined') {
+ // one param defined
+ stop = start;
+ start = 0;
+ };
+ if (typeof step == 'undefined') {
+ step = 1;
+ };
+ if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
+ return [];
+ };
+ var result = [];
+ for (var i = start; step > 0 ? i < stop : i > stop; i += step) {
+ result.push(i);
+ };
+ return result;
+ };
+
+
+ // Extend TestCase by creating a new Test class as we're using it in a few places.
+ var Test = function() { // Test constructor.
+ /**
+ * The following call is the equivalent of "super" in Java. It's not necessary
+ * here as the unittest.TestCase constructor doesn't set any state, but it
+ * is the correct thing to do when implementing classical inheritance in
+ * JavaScript so I've kept it here as a useful reminder of the "pattern".
+ */
+ //unittest.TestCase.prototype.constructor.call(this);
+ };
+
+ Test.prototype = new unittest.TestCase(); // Here's where the inheritance occurs.
+ Test.prototype.constructor = Test; // Otherwise instances of Test would have a constructor of unittest.TestCase.
+
+ Test.prototype.setUp = function() {
+ this.msg = new proton.Message();
+ };
+
+ Test.prototype.tearDown = function() {
+ this.msg.free();
+ this.msg = null;
+ };
+
+
+ // Extend Test more simply by creating a prototype instance and adding test methods as properties.
+
+ var AccessorsTest = new Test();
+
+ AccessorsTest._test = function(name, defaultValue, values) {
+ /**
+ * For the case of Binary values under test we retrieve their toString().
+ * This is because the methods under test "consume" the data, in other
+ * words ownership of the underlying raw data transfers to the Message
+ * object so the sent Binary object becomes "empty" after calling the setter.
+ * In addition Binary values merely contain a "pointer" to the raw data
+ * so even a "deepEqual" comparison won't accurately compare two Binaries.
+ * For these tests we "cheat" and store an array of characters in the
+ * Binary so that we can use their String forms for comparison tests.
+ *
+ * We also get the String value of Uuid for the case of setUserID because
+ * that method transparently creates Binary values from the String form
+ * of non-Binary data passed to it. It's a convenience method, but makes
+ * testing somewhat more fiddly.
+ */
+
+ // First get the values passed to the method.
+ var values = Array.prototype.slice.apply(arguments, [2]);
+ // If the first element of values is actually an Array then use the Array.
+ // This scenario is what happens in the tests that use the range() function.
+ values = (Object.prototype.toString.call(values[0]) === '[object Array]') ? values[0] : values;
+
+ // Work out the accessor/mutator names noting that boolean accessors use "is" not "get".
+ var setter = 'set' + name;
+ var getter = (typeof defaultValue === 'boolean') ? 'is' + name : 'get' + name;
+
+ // Get the message's default value first.
+ var d = this.msg[getter]();
+ d = (d instanceof proton.Data.Binary) ? d.toString() : d;
+
+ // Compare the message's default with the expected default value passed in the test case.
+ assert.deepEqual(d, defaultValue);
+
+ for (var i = 0; i < values.length; i++) {
+ var v = values[i];
+
+ var value = (v instanceof proton.Data.Binary ||
+ (name === 'UserID' && v instanceof proton.Data.Uuid)) ? v.toString() : v;
+ value = (v instanceof Date) ? v.valueOf() : v;
+
+ this.msg[setter](v); // This call will "consume" Binary data.
+
+ var gotten = this.msg[getter]();
+ gotten = (gotten instanceof proton.Data.Binary) ? gotten.toString() : gotten;
+ gotten = (gotten instanceof Date) ? gotten.valueOf() : v;
+
+ assert.deepEqual(value, gotten);
+ }
+ };
+
+ AccessorsTest._testString = function(name) {
+ this._test(name, "", "asdf", "fdsa", "");
+ };
+
+ AccessorsTest._testTime = function(name) {
+ // The ExpiryTime and CreationTime methods can take either a number or a Date Object.
+ this._test(name, new Date(0), new Date(0), 123456789, new Date(987654321));
+ };
+
+ AccessorsTest.testID = function() {
+ console.log("testID");
+ this._test("ID", null, "bytes", null, 123, "string", new proton.Data.Uuid(), new proton.Data.Binary("ВИНАРЫ"));
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testCorrelationID = function() {
+ console.log("testCorrelationID");
+ this._test("CorrelationID", null, "bytes", null, 123, "string", new proton.Data.Uuid(), new proton.Data.Binary("ВИНАРЫ"));
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testDurable = function() {
+ console.log("testDurable");
+ this._test("Durable", false, true, false);
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testPriority = function() {
+ console.log("testPriority");
+ this._test("Priority", proton.Message.DEFAULT_PRIORITY, range(0, 256));
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testTTL = function() {
+ console.log("testTTL");
+ this._test("TTL", 0, range(12345, 54321));
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testFirstAcquirer = function() {
+ console.log("testFirstAcquirer");
+ this._test("FirstAcquirer", false, true, false);
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testDeliveryCount = function() {
+ console.log("testDeliveryCount");
+ this._test("DeliveryCount", 0, range(0, 1024));
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testUserID = function() {
+ console.log("testUserID");
+ this._test("UserID", "", "asdf", "fdsa", 123, new proton.Data.Binary("ВИНАРЫ"), new proton.Data.Uuid(), "");
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testAddress = function() {
+ console.log("testAddress");
+ this._testString("Address");
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testSubject = function() {
+ console.log("testSubject");
+ this._testString("Subject");
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testReplyTo = function() {
+ console.log("testReplyTo");
+ this._testString("ReplyTo");
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testContentType = function() {
+ console.log("testContentType");
+ this._testString("ContentType");
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testContentEncoding = function() {
+ console.log("testContentEncoding");
+ this._testString("ContentEncoding");
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testExpiryTime = function() {
+ console.log("testExpiryTime");
+ this._testTime("ExpiryTime");
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testCreationTime = function() {
+ console.log("testCreationTime");
+ this._testTime("CreationTime");
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testGroupID = function() {
+ console.log("testGroupID");
+ this._testString("GroupID");
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testGroupSequence = function() {
+ console.log("testGroupSequence");
+ this._test("GroupSequence", 0, 0, -10, 10, 20, -20);
+ console.log("OK\n");
+ };
+
+ AccessorsTest.testReplyToGroupID = function() {
+ console.log("testReplyToGroupID");
+ this._testString("ReplyToGroupID");
+ console.log("OK\n");
+ };
+
+
+ var CodecTest = new Test();
+
+ CodecTest.testRoundTrip = function() {
+ console.log("testRoundTrip");
+ this.msg.setID("asdf");
+ this.msg.setCorrelationID(new proton.Data.Uuid());
+ this.msg.setTTL(3);
+ this.msg.setPriority(100);
+ this.msg.setAddress("address");
+ this.msg.setSubject("subject");
+ this.msg.body = "Hello World!";
+
+ var data = this.msg.encode();
+ var msg2 = new proton.Message();
+ msg2.decode(data);
+
+ assert(this.msg.getID() === msg2.getID());
+ assert(this.msg.getCorrelationID().toString() === msg2.getCorrelationID().toString());
+ assert(this.msg.getTTL() === msg2.getTTL());
+ assert(this.msg.getPriority() === msg2.getPriority());
+ assert(this.msg.getAddress() === msg2.getAddress());
+ assert(this.msg.getSubject() === msg2.getSubject());
+ assert(this.msg.body === msg2.body);
+
+ msg2.free();
+ console.log("OK\n");
+ };
+
+ /**
+ * This test tests the transparent serialisation and deserialisation of JavaScript
+ * Objects using the AMQP type system (this is the default behaviour).
+ */
+ CodecTest.testRoundTripBodyObject = function() {
+ console.log("testRoundTripBodyObject");
+ this.msg.setAddress("address");
+ this.msg.body = {array: [1, 2, 3, 4], object: {name: "John Smith", age: 65}};
+
+ var data = this.msg.encode();
+ var msg2 = new proton.Message();
+ msg2.decode(data);
+
+ assert(this.msg.getAddress() === msg2.getAddress());
+ assert(this.msg.getContentType() === msg2.getContentType());
+ assert.deepEqual(this.msg.body, msg2.body);
+
+ msg2.free();
+ console.log("OK\n");
+ };
+
+ /**
+ * This test tests the transparent serialisation and deserialisation of JavaScript
+ * Objects as JSON. In this case the "on-the-wire" representation is an AMQP binary
+ * stored in the AMQP data section.
+ */
+ CodecTest.testRoundTripBodyObjectAsJSON = function() {
+ console.log("testRoundTripBodyObjectAsJSON");
+ this.msg.setAddress("address");
+ this.msg.setContentType("application/json");
+ this.msg.body = {array: [1, 2, 3, 4], object: {name: "John Smith", age: 65}};
+
+ var data = this.msg.encode();
+ var msg2 = new proton.Message();
+ msg2.decode(data);
+
+ assert(this.msg.getAddress() === msg2.getAddress());
+ assert(this.msg.getContentType() === msg2.getContentType());
+ assert.deepEqual(this.msg.body, msg2.body);
+
+ msg2.free();
+ console.log("OK\n");
+ };
+
+ /**
+ * By default the API will encode using the AMQP type system, but is the content-type
+ * is set it will encode as an opaque Binary in an AMQP data section. For certain
+ * types however this isn't the most useful thing. For application/json (see
+ * previous test) we convert to and from JavaScript Objects and for text/* MIME
+ * types the API will automatically convert the received Binary into a String.
+ */
+ CodecTest.testRoundTripMIMETextObject = function() {
+ console.log("testRoundTripMIMETextObject");
+ this.msg.setAddress("address");
+ this.msg.setContentType("text/plain");
+ this.msg.body = "some text";
+
+ var data = this.msg.encode();
+ var msg2 = new proton.Message();
+ msg2.decode(data);
+
+ assert(this.msg.getAddress() === msg2.getAddress());
+ assert(this.msg.getContentType() === msg2.getContentType());
+ assert.deepEqual(this.msg.body, msg2.body);
+
+ msg2.free();
+ console.log("OK\n");
+ };
+
+
+ // load and save are deprecated so not implemented in the JavaScript binding.
+
+ AccessorsTest.run();
+ CodecTest.run();
+} else {
+ console.error("message.js should be run in Node.js");
+}
+
diff --git a/tests/javascript/soak.js b/tests/javascript/soak.js
new file mode 100755
index 0000000..8ecce6a
--- /dev/null
+++ b/tests/javascript/soak.js
@@ -0,0 +1,93 @@
+#!/usr/bin/env node
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Check if the environment is Node.js and if not log an error and exit.
+if (typeof process === 'object' && typeof require === 'function') {
+ var proton = require("qpid-proton");
+
+ var addr = 'guest:guest@localhost:5673';
+ //var addr = 'localhost:5673';
+ var address = 'amqp://' + addr;
+ console.log(address);
+
+ var subscriptionQueue = '';
+ var count = 0;
+ var start = 0; // Start Time.
+
+ var message = new proton.Message();
+ var messenger = new proton.Messenger();
+
+ var pumpData = function() {
+ while (messenger.incoming()) {
+ // The second parameter forces Binary payloads to be decoded as strings
+ // this is useful because the broker QMF Agent encodes strings as AMQP
+ // binary, which is a right pain from an interoperability perspective.
+ var t = messenger.get(message, true);
+ //console.log("Address: " + message.getAddress());
+ //console.log("Content: " + message.body);
+ messenger.accept(t);
+
+ if (count % 1000 === 0) {
+ var time = +new Date();
+ console.log("count = " + count + ", duration = " + (time - start) + ", rate = " + ((count*1000)/(time - start)));
+ }
+
+ sendMessage();
+ }
+
+ if (messenger.isStopped()) {
+ message.free();
+ messenger.free();
+ }
+ };
+
+ var sendMessage = function() {
+ var msgtext = "Message Number " + count;
+ count++;
+
+ message.setAddress(address + '/' + subscriptionQueue);
+ message.body = msgtext;
+ messenger.put(message);
+ //messenger.settle();
+ };
+
+ messenger.on('error', function(error) {console.log(error);});
+ messenger.on('work', pumpData);
+ messenger.on('subscription', function(subscription) {
+ var subscriptionAddress = subscription.getAddress();
+ var splitAddress = subscriptionAddress.split('/');
+ subscriptionQueue = splitAddress[splitAddress.length - 1];
+
+ console.log("Subscription Queue: " + subscriptionQueue);
+ start = +new Date();
+ sendMessage();
+ });
+
+ //messenger.setOutgoingWindow(1024);
+ messenger.setIncomingWindow(1024); // The Java Broker seems to need this.
+ messenger.recv(); // Receive as many messages as messenger can buffer.
+ messenger.start();
+
+ messenger.subscribe('amqp://' + addr + '/#');
+} else {
+ console.error("soak.js should be run in Node.js");
+}
+
diff --git a/tests/javascript/unittest.js b/tests/javascript/unittest.js
new file mode 100644
index 0000000..bc1567c
--- /dev/null
+++ b/tests/javascript/unittest.js
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * The TestCase class provides a simple dependency-free Unit Test framework to
+ * automagically invoke methods that start with "test" on classes that extend it.
+ */
+
+// TestCase Constructor
+var TestCase = function() {};
+
+// Enumerate all functions of the class and invoke those beginning with "test".
+TestCase.prototype.run = function() {
+ for (var property in this) {
+ if ((typeof this[property] === 'function') &&
+ property.lastIndexOf('test', 0) === 0) {
+ this.setUp();
+ this[property]();
+ this.tearDown();
+ }
+ }
+};
+
+TestCase.prototype.setUp = function() {};
+TestCase.prototype.tearDown = function() {};
+
+module.exports.TestCase = TestCase;
+
diff --git a/tools/cmake/Modules/FindEmscripten.cmake b/tools/cmake/Modules/FindEmscripten.cmake
new file mode 100644
index 0000000..7289731
--- /dev/null
+++ b/tools/cmake/Modules/FindEmscripten.cmake
@@ -0,0 +1,40 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# FindEmscripten
+# This module checks if Emscripten and its prerequisites are installed and if so
+# sets EMSCRIPTEN_FOUND Emscripten (https://github.com/kripken/emscripten) is a
+# C/C++ to JavaScript cross-compiler used to generate the JavaScript bindings.
+
+if (NOT EMSCRIPTEN_FOUND)
+ # First check that Node.js is installed as that is needed by Emscripten.
+ find_program(NODE node)
+ if (NOT NODE)
+ message(STATUS "Node.js (http://nodejs.org) is not installed: can't build JavaScript binding")
+ else (NOT NODE)
+ # Check that the Emscripten C/C++ to JavaScript cross-compiler is installed.
+ find_program(EMCC emcc)
+ if (NOT EMCC)
+ message(STATUS "Emscripten (https://github.com/kripken/emscripten) is not installed: can't build JavaScript binding")
+ else (NOT EMCC)
+ set(EMSCRIPTEN_FOUND ON)
+ endif (NOT EMCC)
+ endif (NOT NODE)
+endif (NOT EMSCRIPTEN_FOUND)
+
diff --git a/tools/cmake/Modules/FindNodePackages.cmake b/tools/cmake/Modules/FindNodePackages.cmake
new file mode 100644
index 0000000..bfbb36f
--- /dev/null
+++ b/tools/cmake/Modules/FindNodePackages.cmake
@@ -0,0 +1,69 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# FindNodePackages
+# This module finds and installs (using npm) node.js packages that are used by
+# the JavaScript binding. The binding should still compile if these packages
+# cannot be installed but certain features might not work as described below.
+#
+# * The ws package is the WebSocket library used by emscripten when the target is
+# node.js, it isn't needed for applications hosted on a browser (where native
+# WebSockets will be used), but without it it won't work with node.js.
+#
+# * The jsdoc package is a JavaScript API document generator analogous to Doxygen
+# or JavaDoc, it is used by the docs target in the JavaScript binding.
+
+if (NOT NODE_PACKAGES_FOUND)
+ # Check if the specified node.js package is already installed, if not install it.
+ macro(InstallPackage varname name)
+ execute_process(
+ COMMAND npm list --local ${name}
+ OUTPUT_VARIABLE check_package
+ )
+
+ set(${varname} OFF)
+
+ if (check_package MATCHES "${name}@.")
+ message(STATUS "Found node.js package: ${name}")
+ set(${varname} ON)
+ else()
+ message(STATUS "Installing node.js package: ${name}")
+
+ execute_process(
+ COMMAND npm install ${name}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ OUTPUT_VARIABLE var
+ )
+
+ if (var)
+ message(STATUS "Installed node.js package: ${name}")
+ set(${varname} ON)
+ endif (var)
+ endif()
+ endmacro()
+
+ # Check if ws WebSocket library https://github.com/einaros/ws is installed
+ InstallPackage("NODE_WS_FOUND" "ws")
+
+ # Check if jsdoc3 API documentation generator https://github.com/jsdoc3/jsdoc is installed
+ InstallPackage("NODE_JSDOC_FOUND" "jsdoc")
+
+ set(NODE_PACKAGES_FOUND ON)
+endif (NOT NODE_PACKAGES_FOUND)
+