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

Reply via email to