Glad that I was able to help out.
I'm a little bit curious why you're choosing to go with the REST
interface directly? What language are you programming in.
Not that I've got anything against the REST API, but I mainly put it in
place to be able to talk QMF from a browser environment.
If you are programming in Java you'd probably find it easier to use the
Java QMF API directly, if you're doing python then use the API that
qpid-config etc. use, from C++ it's a little more awkward but not too
hard to send QMF2 protocol requests out (it's ultimately just a bunch of
Map Messages) I've attached a couple of examples put together by Gordon
Sim to illustrate this in C++ the queue_depth is self explanatory, the
one marked example.cpp does something similar to qpid-route - not
exactly what you want for adding a queue, but it illustrates invoking
QMF2 methods via the raw QMF2 protocol and you can use that in
conjunction with what I've shown below on the REST API (this bit
{
"_method_name":"create",
"_arguments": {"type": "queue", "name": "davin_test2", "properties":
{"durable": true}}
}
)
It is quite fun doing this stuff via curl, but it's pretty hardcore :-)
I'd *probably* take easier options in Python/Java/C++/JavaScript unless
I wanted to prove a point (clearly REST is handy if you want to mash up
info from lots of different sources in a web app).
BTW in case you are curious the reason that the URI's are relatively
complex e.g.
GET http://127.0.0.1:8080/qpid/connection/default/console/objects/queue
rather than say
GET http://127.0.0.1:8080/qpid/console/objects/queue
Is because it's perfectly possible to PUT an arbitrary number of QMF
Console connections and access them independently so the GUI can talk to
multiple discrete Consoles and I can have multiple browser instances
served by the same REST API instance.
Cheers,
Frase
On 27/01/14 22:06, Shearer, Davin wrote:
Thanks Frase,
I read and followed along through your explanation and was able to browse
various objects and finally create a queue via the REST API backed by the
C++ broker using only curl (yay!). Hopefully I can use the interface to
create one dynamically with the delete on close attribute. I'll give that
a go tomorrow!
On Thu, Jan 23, 2014 at 6:54 PM, Fraser Adams <fraser.ad...@blueyonder.co.uk
wrote:
Hey Davin,
Robbie has hit the nail on the head. One somewhat unfortunate thing that
you probably ought to be aware of is that the management and monitoring for
the Qpid C++ and Java Brokers got somewhat erm "fragmented" for various
reasons back in the mists of time.
The C++ Broker uses Qpid Management Framework (QMF), but that has had an
interesting evolution and at some point in history QMFv1 migrated to QMFv2
which caused issues for the Java Broker and they ended up going down a
different path.
I primarily use the C++ broker, but conversely my team mainly use Java, so
I started out doing a Java implementation of the QMF2 API without knowing
much of that background. There was a bit of comedy 'cause the Qpid Java
guys didn't take a lot of notice of what I was doing 'cause it was QMF and
the C++ guys didn't really notice 'cause it was Java :-D I was a bit in no
mans land really :-/
It wasn't really until I put the GUI together that anyone really noticed
and the penny dropped.
So as Robbie says the REST API that I put together is *actually* a REST
binding for QMF2 and (apart from being a REST API) doesn't bear too much
relationship to the Java Broker's REST interface (for most of history we
were largely unaware of what each other was up to).
It's a far from ideal position and Rob, Gordon, Robbie and I have chatted
a fair bit about the need to get a more cohesive view. Ultimately the right
answer will be to adopt the AMQP Management Specification across the board,
though that's likely to be some way off (I certainly need to find some time
to properly get my head around it).
By way of "making a stand for the unity of Qpid Brokers :-)" I put
together a QMF2 plugin for the Java Broker, it was one of those things that
one does "on principle" - really it was as much as anything a statement to
say "really guys it *is* actually possible to provide a cohesive view". One
thing to bear in mind with the Java Broker QMF plugin is that I've tried
fairly hard to map as much as possible from the Java Broker internal
Management Model to QMF Management Objects, but there are differences in
the Management Models, so there are certainly more itty bitty things that
can be controlled by the Java Broker's native management interface than via
QMF (mainly 'cause Robbie keeps adding features ;->) but OTOH pretty much
anything you can do with qpid-config will work with both the C++ and Java
Brokers.
So to get back to the QMF REST API, the best place to look is the JavaDoc
(yes really, there is actually a ton of JavaDoc for this stuff)
start in <qpid>/tools/src/java/docs/api/index.html
Then look for package org.apache.qpid.restapi and class QpidServer
To get you started if you do (assuming the same host and port as below):
http://127.0.0.1:8080/qpid/connection/default/console/objects/queue
You will retrieve a list of all of the QMF Queue Management Objects in the
form of a JSON list (you can do it in a browser too)
http://127.0.0.1:8080/qpid/connection/default/console/objects/exchangewill
retrieve the exchanges
http://127.0.0.1:8080/qpid/connection/default/console/objects/connectionwill
retrieve the connections
and so on.
the <default> bit is a "convenience", so this is a "true" REST API that
mirrors the QMF API pretty closely so you really ought to first PUT a QMF
Console Connection before you do anything else, but of course PUT is a pain
from a browser, so if you specify "default" for the connection part of the
URI the API transparently creates (or uses an already created) default QMF
Console instance.
The GUI actually has a JavaScript class that provides a JavaScript
implementation of the QMF2 API, which is backed by the REST API, so the GUI
is generally abstracted from the REST API, there's a factory method for
that which does the HTTP PUT "under the hood"
You can also use POST in order to invoke QMF methods (for adding/deleting
queues etc.).
To be honest I'm a bit rusty - I tend to use the GUI or at worse the
JavaScript wrapper which abstracts the detail,
and I've not done it "the hard way" for the best part of 15 months, so the
following is pulled out from the code.
The POST syntax is:
POST: <host>:<port>/qpid/connection/<name>/object/<ObjectId>
HTTP body: {"_method_name":<method>,"_arguments":<inArgs>}
<method>: A string containing the QMF2 method name e.g.
"getLogLevel", "setLogLevel", "create", "delete".
<inArgs>: A JSON string containing the method arguments e.g.
{"level":"debug+:Broker"} for setLogLevel.
HTTP response: A JSON string containing the response e.g.
{"level":"notice+"} for getLogLevel (may be empty).
This method invokes the QMF2 method <method> with arguments <inArgs>
on the object <ObjectId>
And it works like QMF, so what you'd first need to do is to recover the
Broker Management Object
http://127.0.0.1:8080/qpid/connection/default/console/objects/broker
You need to do this because the majority of the QMF CRUD methods are
applied on the Broker Management Object,
having done this you'd need to do a POST as above (using the ObjectId from
your own Broker Object)
POST http://127.0.0.1:8080/qpid/connection/default/object/@0@
org.apache.qpid.broker:broker:amqp-broker
And in the HTTP body you need a JSON string.
To Add a queue I *think* that the JSON body goes something like this (for
your davin_test2 queue):
{
"_method_name":"create",
"_arguments": {"type": "queue", "name": "davin_test2", "properties":
{"durable": true}}
}
Clearly this isn't something that one would *generally* do "by hand".
I've just done:
curl -X POST -u admin:admin -d '{"_method_name":"create","_arguments":
{"type": "queue", "name": "davin_test2", "properties": {"durable": true}}}'
http://127.0.0.1:8080/qpid/connection/default/object/@0@
org.apache.qpid.broker:broker:amqp-broker
On my system and it created a queue called "davin_test2", so I think what
I've just said is accurate, yay!!
The available properties for Queues and exchanges isn't brilliantly
documented in the QMF Management Schema
(or anywhere really) and I kind of "reverse engineered" them, looking
through the GUI code (qmf-ui.js in qmfui.AddQueue method)
I can see:
qpid.max_size
qpid.max_count
qpid.flow_stop_size
qpid.flow_stop_count
qpid.flow_resume_size
qpid.flow_resume_count
durable
qpid.file_size
qpid.file_count
qpid.policy_type
qpid.last_value_queue
qpid.last_value_queue_no_browse
qpid.queue_event_generation
alternate-exchange
If you look in the qpid-config code I think you'd come across these too.
I've *probably* just made your head explode, so I'd better stop there,
*hopefully* this is helpful and useful background.
Best regards,
Frase
P.S. Glad that you like the QMF GUI - it looks pretty nice on an iOS
device, it uses CSS3 animations so the transitions are GPU accelerated.
On 23/01/14 19:42, Robbie Gemmell wrote:
On 23 January 2014 19:27, Shearer, Davin <dshea...@novetta.com> wrote:
[davin@laptop24 qpid]$ curl -X GET -u admin:admin
http://127.0.0.1:8080/rest/queue/davin_test
404 Not Found: File /rest/queue/davin_test not found.[davin@laptop24qpid
]$
Darn, that didn't work... can we put new queues?
[davin@laptop24 qpid]$ curl -X PUT -u admin:admin -d '{"durable":true}'
http://127.0.0.1:8080/rest/queue/default/davin_test2
405 Bad Method.[davin@laptop24 qpid]$
What am I missing?
Those URLs look a lot like ones that would be used against the REST API
from the Java brokers management-http plugin. The URLs/payloads used by
the
'QMF REST API' from the tools Fraser put together aren't likely to be that
similar to them.
I'll now have to defer to someone who can tell you what they would be
like...
Robbie
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@qpid.apache.org
For additional commands, e-mail: users-h...@qpid.apache.org
/*
*
* 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.
*
*/
#include <qpid/messaging/Address.h>
#include <qpid/messaging/Connection.h>
#include <qpid/messaging/Message.h>
#include <qpid/messaging/Receiver.h>
#include <qpid/messaging/Sender.h>
#include <qpid/messaging/Session.h>
#include <qpid/types/Variant.h>
#include <iostream>
using namespace qpid::messaging;
using namespace qpid::types;
using std::string;
int main(int argc, char** argv)
{
if (argc == 1) {
std::cout << "Please specify a queue name." << std::endl;
return 1;
}
std::string queue(argv[1]);
Connection c(argc > 2 ? argv[2] : "localhost");
try {
c.open();
Session session = c.createSession();
Address responses("#; {create: always, node: {x-declare: {auto-delete:True}}}");
Receiver r = session.createReceiver(responses);
Sender s = session.createSender("qmf.default.direct/broker");
Message request;
request.setReplyTo(responses);
request.setContentType("amqp/map");
request.setProperty("x-amqp-0-10.app-id", "qmf2");
request.setProperty("qmf.opcode", "_query_request");
Variant::Map oid;
oid["_object_name"] = std::string("org.apache.qpid.broker:queue:") + queue;
Variant::Map content;
content["_what"] = "OBJECT";
content["_object_id"] = oid;
encode(content, request);
s.send(request);
Message response = r.fetch();
Variant::List contentIn;
decode(response, contentIn);
if (contentIn.size() == 1) {
Variant::Map details = contentIn.front().asMap()["_values"].asMap();
std::cout << "Message depth for " << queue << " is " << details["msgDepth"] << std::endl;
} else if (contentIn.size() == 0) {
std::cout << "No such queue: " << queue << std::endl;
} else {
std::cout << "Unexpected number of entries: " << contentIn << std::endl;
}
session.acknowledge();
} catch(const std::exception& error) {
std::cout << "ERROR: " << error.what() << std::endl;
}
c.close();
return 0;
}
/*
*
* 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.
*
*/
#include <qpid/messaging/Address.h>
#include <qpid/messaging/Connection.h>
#include <qpid/messaging/Message.h>
#include <qpid/messaging/Receiver.h>
#include <qpid/messaging/Sender.h>
#include <qpid/messaging/Session.h>
#include <qpid/types/Variant.h>
#include <iostream>
#include <sstream>
using namespace qpid::messaging;
using namespace qpid::types;
using std::string;
class Controller
{
public:
Controller(Session&);
void link(const std::string& name, const std::string& host, uint16_t port,
const std::string& transport=std::string(), const std::string& mechanisms=std::string(),
const std::string& username=std::string(), const std::string& password=std::string());
void bridge(const std::string& name, const std::string& link,
const std::string& src, const std::string& dest, const std::string& tag=std::string());
private:
Address replyTo;
Sender sender;
Receiver receiver;
void invoke(const std::string& method, const Variant::Map& args);
};
int main(int argc, char** argv)
{
Connection c(argc > 1 ? argv[1] : "localhost");
try {
c.open();
Session session = c.createSession();
Controller controller(session);
controller.link("my-link", "localhost", 5673);
controller.bridge("my-bridge", "my-link", "amq.fanout", "amq.fanout");
} catch(const std::exception& error) {
std::cout << "ERROR: " << error.what() << std::endl;
}
c.close();
return 0;
}
Controller::Controller(Session& session) :replyTo("#; {create:always, node:{x-declare:{auto-delete:true}}}"),
sender(session.createSender("qmf.default.direct/broker")),
receiver(session.createReceiver(replyTo)) {}
void Controller::link(const std::string& name, const std::string& host, uint16_t port, const std::string& transport, const std::string& mechanism, const std::string& username, const std::string& password)
{
Variant::Map args;
args["type"] = "link";
args["name"] = name;
Variant::Map props;
props["host"] = host;
props["port"] = port;
if (!transport.empty()) props["transport"] = transport;
if (!mechanism.empty()) props["authMechanism"] = mechanism;
if (!username.empty()) props["username"] = username;
if (!password.empty())props["password"] = password;
args["properties"] = props;
invoke("create", args);
}
void Controller::bridge(const std::string& name, const std::string& link,
const std::string& src, const std::string& dest, const std::string& tag)
{
Variant::Map args;
args["type"] = "bridge";
args["name"] = name;
Variant::Map props;
props["link"] = link;
props["src"] = src;
props["dest"] = dest;
if (!tag.empty()) props["tag"] = tag;
args["properties"] = props;
invoke("create", args);
}
void Controller::invoke(const std::string& method, const Variant::Map& args)
{
Variant::Map content;
Variant::Map objectId;
objectId["_object_name"] = "org.apache.qpid.broker:broker:amqp-broker";
content["_object_id"] = objectId;
content["_method_name"] = method;
content["_arguments"] = args;
Message request;
request.setReplyTo(replyTo);
request.getProperties()["x-amqp-0-10.app-id"] = "qmf2";
request.getProperties()["qmf.opcode"] = "_method_request";
encode(content, request);
sender.send(request);
Message response;
if (receiver.fetch(response, Duration::SECOND*5)) {
if (response.getProperties()["x-amqp-0-10.app-id"] == "qmf2") {
std::string opcode = response.getProperties()["qmf.opcode"];
if (opcode == "_exception") {
Variant::Map m;
decode(response, m);
std::cout << "Error: " << m["_values"] << std::endl;
}
} else {
std::cout << "Invalid response received, not a qmfv2 message: app-id="
<< response.getProperties()["x-amqp-0-10.app-id"] << std::endl;
}
} else {
std::cout << "No response received" << std::endl;
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@qpid.apache.org
For additional commands, e-mail: users-h...@qpid.apache.org