Hi Again Ernie,
I take it back, it *was* something you've done ;-p
Or rather it was something that you didn't do.
If you look at the last line of the original send.js it says:
tracker = messenger.put(message);
But your last line below says simply:
messenger.put(message);
The error is simply because null is being passed to
var status = messenger.status(tracker);
If you add the "tracker = " bit it works fine, yay!!
I'd totally agree that the error was worse than useless :-( I'll have a
think about how best to make things more debuggable.
I suspect that issues around the tracker are some of the quirkiest
<techie-alert>
I had a world of complexity getting the tracker stuff working. As you
know I'm compiling proton-c to JavaScript using emscripten and 64 bit
integers present complexities in JavaScript.
As it happens compiling directly from C code it is all taken care of
transparently, but for the bindings I'm having to map from native
JavaScript, if you look at the code for messenger.status it looks like this:
/**
* 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) {
return _pn_messenger_status(this._messenger,
tracker.getLowBitsUnsigned(), tracker.getHighBits());
};
So basically emscripten uses the LLVM le32 front-end (little endian 32
bit generic front end) and for function calls taking a 64 bit integer it
actually takes *two* parameters - the LSB then MSB. When compiling C
code you don't need this but the binding is logically integrating at the
linker level.
I *suspect* that pn_messenger_status (hopefully) gives and error if it
gets passed a null tracker, but in the case of the binding the tracker
is actually a proton.Data.Long which is a mechanism to allow accurate
representation of 64 bit integers in JavaScript - and of course the
problem is that tracker.getLowBitsUnsigned() is trying to dereference a
null variable, which makes the JS interpreter very sad.
I mentioned in a previous mail that successfully compiling the C code
into JavaScript was the first thing I did, as it happens it has taken me
an age of weekends doing the *binding* stuff because of quirky issues
like this. It's not especially complicated so much as a bit fiddly and
tedious (I did say that JavaScript programmers probably wouldn't want to
use a C API :->).
I personally think that the best bit of the JavaScript binding is being
able to use native JavaScript Objects/Arrays/etc. if you look at the QMF
example it's so much easier than what you have to do in C/C++/Java
(though to be fair python is as nice as JavaScript).
Another couple of things to be aware of are Binaries and explicit types.
I'm planning on writing some docs for this but do
make docs
which should generate documentation for the bindings using jsdoc.
Oh and the spout.js "example" - which is totally broken and doesn't do
spout yet :-) currently has a bunch of useful stuff commented out (needs
to go in docs really, just haven't got round to it)
so as you probably know JavaScript doesn't have explicit numeric types
it just has "number", which is a double. If you are sending to/from
JavaScript you don't have to care, but if you want to send a number of a
particular type you have to be more explicit.
I've added a bunch of conversion functions to the Number prototype, so
for example if you do:
message.body = (12147483649).long();
It will send as an AMQP long
message.body = (12147483649).float();
It will send as an AMQP float (i.e not a double).
In actual fact the binding code tries to make some inferences about the
type, so for example if you do:
message.body = 2147483647;
it will send an int, but if you do:
message.body = 12147483649;
it will send a long ('cause it's more than 32 bits)
and if you do:
message.body = 1.1;
it will send a double, cause it realises it's a floating point type,
however one gotcha is this:
message.body = 1.0;
This will *actually* end up getting sent as an AMQP int, the reason for
this is that JavaScript *transparently* converts floats ending in ".0"
to integers internally and there's nothing you can do about it :-( so if
your consumer expects particular types you need to be aware of this (you
don't need to do anything fancy when receiving numbers though, just when
sending).
Binary is a little bit interesting too. I support native JavaScript
Binary data using TypedArrays, but I've tried to do it efficiently. The
way emscripten works is that it actually uses a big TypedArray as a
virtual heap, so when you do "malloc" in C is uses space there, just
twiddling indexes. For Binary what I do is to allocate data on the
emscripten heap so you don't have to copy to and from a separate
ArrayBuffer, which is obviously more efficient. The awkward thing then
becomes memory management. I try to make this as transparent as possible
from a JavaScript perspective so the underlying memory is "owned" by the
Message object so will be transparently managed when you use Message as
you normally would tend to. The gotcha though is that the next time you
do messenger.get() the memory for your Binary will be overwritten, so if
you need to hold on to a Binary across multiple calls to messenger.get()
you would then need to copy the data from the Binary into a new
ArrayBuffer. Hopefully the jsdoc covers that fairly well though.
</techie-alert>
HTH,
Frase
On 30/08/14 16:25, Ernest Allen wrote:
Hi Fraser,
I'm trying to run the proton javascript bindings from within a browser. I'm
getting various errors depending on the way I'm doing it and I was hoping you
could shed some light on the correct approach.
I have node recv.js running in a separate window.
The first way I tried was just including proton.js directly. When I load the
page the console shows the following for the HTML listed below:
pre-main prep time: 6 ms
proton is [object Object]
before put
Uncaught TypeError: Cannot read property 'fa' of null
Uncaught TypeError: Cannot read property 'fa' of null
Here is the HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>An XHTML 1.0 Strict standard template</title>
<meta http-equiv="content-type"
content="text/html;charset=utf-8" />
<script type="text/javascript"
src="../../../node_modules/qpid-proton/lib/proton.js"></script>
<script type="text/javascript">
//<![CDATA[
console.log("proton is " + window.proton);
var address = "amqp://0.0.0.0";
var subject = "BrowserMessage";
var msgtext = "Hello From Browser!";
var tracker = null;
var running = true;
var message = new proton.Message();
var messenger = new proton.Messenger();
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();
}
};
messenger.on('error', function(error) {console.log("this is the error" +
error);});
messenger.on('work', pumpData);
messenger.setOutgoingWindow(1024);
messenger.start();
message.setAddress(address);
message.setSubject(subject);
message.body = msgtext;
console.log("before put");
messenger.put(message);
//]]>
</script>
</head>
<body>
</body>
</html>
The output in the window running node recv.js is:
[0x535398]:ERROR[-2] AMQP header mismatch: '' (connection aborted)
CONNECTION ERROR connection aborted (remote)
When I browserify the proton.js into qpid-proton-browser.js and load that file
instead, I get the following when the page loads:
Uncaught TypeError: Cannot read property 'replace' of undefined
proton is undefined
Uncaught ReferenceError: proton is not defined
The Uncaught TypeError is due to a referece to "process.argv[1]". If I manually edit the
qpid-proton-browser.js file to define all the process properties that it wants, I still get the
"proton is undefined" error.
Do you know what I'm doing wrong?
Thanks,
-Ernie