I am trying to add MUC capabilities to my application with QXmpp, I am following this info in the QXmppMucManager.h header file
/// \brief The QXmppMucManager class makes it possible to interact with
/// multi-user chat rooms as defined by XEP-0045: Multi-User Chat.
///
/// To make use of this manager, you need to instantiate it and load it into
/// the QXmppClient instance as follows:
///
/// \code
/// QXmppMucManager *manager = new QXmppMucManager;
/// client->addExtension(manager);
/// \endcode
///
/// You can then join a room as follows:
///
/// \code
/// QXmppMucRoom *room = manager->addRoom("room#conference.example.com");
/// room->setNickName("mynick");
/// room->join();
/// \endcode
///
/// \ingroup Managers
In a constructor of one of my classes I am adding a MuCManager like this
QXmppMucManager *manager = new QXmppMucManager;
m_xmppClient.addExtension(manager);
and in a slot to create an Muc group I am doing this
QXmppMucRoom *room = manager->addRoom("livefit#mthinkpad");
room->setNickName("mThinkpad");
room->join();
I expect this to create a room and join if it doesn't exist and join if it already exists but it is just crashing my application.I can't wrap my brains around how qxmpp implements the xep--0045.I would appreciate it if somebody helped point what I am doing wrong or how I can create an Muc room on my server from my client.
My server is a local ejabberd installation and I can access it at the "mthinkpad" domain.
In case somebody has the same issues ,I was able to create the rooms.Here is the code that does that.I create and configure the room on the fly(I am using Openfire and it allows it) but you can request the configuration form if you want.
String jid=roomName.text()+"#conference."+serverName;
if(jid.isEmpty())
return ;
QList<QXmppMucRoom*> rooms = manager->rooms();
QXmppMucRoom* r;
foreach(r, rooms)
{
if(r->jid() == jid)
{
//LOG_MODEL_DEBUG("Group chat", "had joined room[%s]", qPrintable(jid));
return ;
}
}
m_pRoom = manager->addRoom(jid);
if(m_pRoom)
{
//nick
m_pRoom->setNickName("theDip");
//join the room.
m_pRoom->join();
}
//Prepare the dataform.
QXmppDataForm form(QXmppDataForm::Submit);
QList<QXmppDataForm::Field> fields;
{
QXmppDataForm::Field field(QXmppDataForm::Field::HiddenField);
field.setKey("FORM_TYPE");
field.setValue("http://jabber.org/protocol/muc#roomconfig");
fields.append(field);
}
QXmppDataForm::Field field;
field.setKey("muc#roomconfig_roomname");
field.setValue(roomName.text());
fields.append(field);
field.setKey("muc#roomconfig_subject");
field.setValue(roomSubject.text());
fields.append(field);
field.setKey("muc#roomconfig_roomdesc");
field.setValue(roomDesc.text());
fields.append(field);
{
QXmppDataForm::Field field(QXmppDataForm::Field::BooleanField);
field.setKey("muc#roomconfig_persistentroom");
field.setValue(true);
fields.append(field);
}
form.setFields(fields);
//The dataform ends here.
m_pRoom->setConfiguration(form);
Related
I am having issues with setting up Eclipse CDT to correctly do development in C++11. It's highlighting std::error_code and honestly everything in the std namespace. While the application compiles just fine on the command line.
Screen shot included.
The full project can be found on github.com/rbaindourov/OpenWeatherMapDSLink including the hidden files used by Eclipse.
I can host the the OVA of the virtual machine if you like as well.
Thanks in advance to any industrious individual who is willing to take the time to help me configure Eclipse CDT correctly.
And apologies if my question is not to your liking.
The source for the main.cpp file originally came from:
https://github.com/CiscoDevNet/kinetic-efm-cpp-sdk/blob/master/efm-cpp-sdk-1.0.15-Ubuntu16.04-dslink-dev/examples/simple_responder/main.cpp
// #copyright_start
// Copyright (c) 2019 Cisco and/or its affiliates. All rights reserved.
// #copyright_end
#include <efm_link.h>
#include <efm_link_options.h>
#include <efm_logging.h>
#include "error_code.h"
#include <iostream>
#include <random>
#include <sstream>
/// #brief The simple responder link example demonstrates the EFM SDK API for responder implementations. Shows node,
/// action creation, and stream handling.
class SimpleResponderLink
{
public:
/// Constructs the responder link implementation.
/// #param link The link to work with.
SimpleResponderLink(cisco::efm_sdk::Link& link)
: link_(link)
, responder_(link.responder())
{
}
/// The initialize callback that will be called as soon as the initialization including serialization is complete.
/// Will create the first level node hierarchy. Only nodes not created by the deserialization will actually be
/// created.
/// #param link_name The name of the link.
/// #param ec The error code will be set to an error if the initialization failed.
void initialize(const std::string& link_name, const std::error_code& ec)
{
if (!ec) {
LOG_EFM_DEBUG(
"SimpleResponderLink", cisco::efm_sdk::DebugLevel::l1, "Responder link '" << link_name << "' initialized");
} else {
LOG_EFM_ERROR(ec, "could not initialize responder link");
}
cisco::efm_sdk::NodeBuilder builder{"/"};
builder.make_node("sdk version")
.display_name("SDK Version")
.type(cisco::efm_sdk::ValueType::String)
.value(link_.get_version_info());
builder.make_node("text")
.display_name("String")
.type(cisco::efm_sdk::ValueType::String)
.value("Hello, World!")
.writable(
cisco::efm_sdk::Writable::Write, std::bind(&::SimpleResponderLink::set_text, this, std::placeholders::_1))
.on_subscribe(std::bind(&::SimpleResponderLink::on_subscribe_text, this, std::placeholders::_1));
builder.make_node("set_text")
.display_name("Set Text")
.action(cisco::efm_sdk::Action(
cisco::efm_sdk::PermissionLevel::Read,
std::bind(
&SimpleResponderLink::set_text_called,
this,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3,
std::placeholders::_4))
.add_param(cisco::efm_sdk::ActionParameter{"String", cisco::efm_sdk::ValueType::String})
.add_column({"Success", cisco::efm_sdk::ValueType::Bool})
.add_column({"Message", cisco::efm_sdk::ValueType::String}));
responder_.add_node(
std::move(builder),
std::bind(&SimpleResponderLink::nodes_created, this, std::placeholders::_1, std::placeholders::_2));
}
/// Callback that will be called upon construction of the first level nodes.
/// #param paths The paths of the nodes that were actually created. A path that was added to the NodeBuilder is
/// not part of the paths vector means that the node was already created. Normally, there is no need to check for
/// the presence of a path. If the error code signals no error, just continue with your work.
/// #param ec The error code will be set to an error if the node creation failed.
void nodes_created(const std::vector<cisco::efm_sdk::NodePath>& paths, const std::error_code& ec)
{
if (!ec) {
LOG_EFM_DEBUG("SimpleResponderLink", cisco::efm_sdk::DebugLevel::l1, "created nodes");
for (const auto& path : paths) {
LOG_EFM_DEBUG("SimpleResponderLink", cisco::efm_sdk::DebugLevel::l2, "created path - " << path);
}
}
}
/// Called every time the link connects to the broker.
/// Will set the value on the '/text' path.
/// #param ec The error code will be set to an error if the connect failed.
void connected(const std::error_code& ec)
{
if (!ec) {
disconnected_ = false;
LOG_EFM_INFO(responder_error_code::connected);
responder_.set_value(text_path_, cisco::efm_sdk::Variant{"Hello, World!"}, [](const std::error_code&) {});
}
}
/// Called every time the link is disconnected from the broker.
/// Will set a flag to signal the disconnected status.
/// #param ec The error code will be set to an error if the disconnect failed.
void disconnected(const std::error_code& ec)
{
LOG_EFM_INFO(responder_error_code::disconnected, ec.message());
disconnected_ = true;
}
/// Will be called the node '/text' is set via an \#set action.
/// #param value The value that was set.
void set_text(const cisco::efm_sdk::Variant& value)
{
LOG_EFM_INFO(responder_error_code::set_text, value);
}
/// Action callback for the '/set_text' action. Will set the value of the path '/text' to the given one. It will also
/// echo back the set parameter.
/// The stream will be closed automatically by the link.
/// #param stream The stream to add a result to.
/// #param parent_path The path of the node the action was called for.
/// #param params The parameters set by the peer.
/// #param ec The error code will be set to an error if the action failed.
void set_text_called(
const cisco::efm_sdk::MutableActionResultStreamPtr& stream,
const cisco::efm_sdk::NodePath& parent_path,
const cisco::efm_sdk::Variant& params,
const std::error_code& ec)
{
(void)parent_path;
if (!ec) {
LOG_EFM_DEBUG("SimpleResponderLink", cisco::efm_sdk::DebugLevel::l3, "set_text_called");
const auto* input = params.get("String");
if (input) {
auto text = *input;
responder_.set_value(text_path_, text, [stream, text](const std::error_code& ec) {
if (!ec) {
stream->set_result(cisco::efm_sdk::UniqueActionResultPtr{new cisco::efm_sdk::ActionValuesResult{
cisco::efm_sdk::ActionValuesResult(cisco::efm_sdk::ActionSuccess).add_value(true).add_value(text)}});
} else {
stream->set_result(cisco::efm_sdk::UniqueActionResultPtr{
new cisco::efm_sdk::ActionValuesResult{cisco::efm_sdk::ActionValuesResult(cisco::efm_sdk::ActionError)
.add_value(false)
.add_value("Could not set value")}});
}
});
return;
}
}
stream->set_result(cisco::efm_sdk::UniqueActionResultPtr{
new cisco::efm_sdk::ActionValuesResult{cisco::efm_sdk::ActionValuesResult(cisco::efm_sdk::ActionError)
.add_value(false)
.add_value("Could not set value")}});
}
/// Will be called if a subscribe or unsubscribe is issued for the '/text' node.
/// #param subscribe True if a subscribe was done or false if an unsubscribe was done.
void on_subscribe_text(bool subscribe)
{
if (subscribe) {
LOG_EFM_INFO(responder_error_code::subscribed_text);
} else {
LOG_EFM_INFO(responder_error_code::unsubscribed_text);
}
}
private:
cisco::efm_sdk::Link& link_;
cisco::efm_sdk::Responder& responder_;
cisco::efm_sdk::NodePath text_path_{"/text"};
bool disconnected_{true};
};
int main(int argc, char* argv[])
{
cisco::efm_sdk::FileConfigLoader loader;
cisco::efm_sdk::LinkOptions options("Simple-Responder-Link", loader);
if (!options.parse(argc, argv, std::cerr)) {
return EXIT_FAILURE;
}
cisco::efm_sdk::Link link(std::move(options), cisco::efm_sdk::LinkType::Responder);
LOG_EFM_INFO(::responder_error_code::build_with_version, link.get_version_info());
SimpleResponderLink responder_link(link);
link.set_on_initialized_handler(
std::bind(&SimpleResponderLink::initialize, &responder_link, std::placeholders::_1, std::placeholders::_2));
link.set_on_connected_handler(std::bind(&SimpleResponderLink::connected, &responder_link, std::placeholders::_1));
link.set_on_disconnected_handler(
std::bind(&SimpleResponderLink::disconnected, &responder_link, std::placeholders::_1));
link.run();
return EXIT_SUCCESS;
}
I am writing an Application that can receive 2 types of event coming into the system from separate sources. I want to have a Context to handle each of them. See the code below :
event MyEvent1{
//stuff for context1
}
event MyEvent2{
//stuff for context2
}
event Cascade{
//PRIORITY stuff for context1 & 2
}
monitor Application{
context parallel1 := context("E1processor");
context parallel2 := context("E2processor");
action onload{
spawn handleE1() to parallel1;
spawn handleE2() to parallel2;
on all MyEvent1() as e {
send e to parallel1;
}
on all MyEvent2() as e {
send e to parallel2;
}
}//onload
action handleE1( ){
on all MyEvent1() as e1 {
//do work, create and route CASCADE event
route Cascade();
//I want to do this!
route Cascade() to parallel2; // < ----- ERROR
}
on all Cascade(){
//URGENT stuff
}
}
action handleE2(){
on all MyEvent2() as e1 {
}
on all Cascade(){
//URGENT stuff
}
}
}//Application
My problem lies with the fact that I want to have the Cascade() event pushed to the front of the processing queue because it is a priority. But when I try to do the following:
//do work, create and route CASCADE event
route Cascade(); //<--- Works
//I want to do this!
route Cascade() to parallel2; // < ----- ERROR
It gives me an error - how can I route the event as a priority from one context to the other?
Unfortunately there's no way to priority send to another context. The answer might be more architectural in nature - can the Cascade handling simply be done in the main context for example?
//StockPriceEmitter is a "dead loop" thread which generate data, and invoke StockPriceService.onUpdates() to send data.
#Service
public class StockPriceService implements StockPriceEmitter.Listener
{
#Inject
private BayeuxServer bayeuxServer;
#Session
private LocalSession sender;
public void onUpdates(List<StockPriceEmitter.Update> updates)
{
for (StockPriceEmitter.Update update : updates)
{
// Create the channel name using the stock symbol
String channelName = "/stock/" + update.getSymbol().toLowerCase(Locale.ENGLISH);
// Initialize the channel, making it persistent and lazy
bayeuxServer.createIfAbsent(channelName, new ConfigurableServerChannel.Initializer()
{
public void configureChannel(ConfigurableServerChannel channel)
{
channel.setPersistent(true);
channel.setLazy(true);
}
});
// Convert the Update business object to a CometD-friendly format
Map<String, Object> data = new HashMap<String, Object>(4);
data.put("symbol", update.getSymbol());
data.put("oldValue", update.getOldValue());
data.put("newValue", update.getNewValue());
// Publish to all subscribers
ServerChannel channel = bayeuxServer.getChannel(channelName);
channel.publish(sender, data, null); // this code works fine
//this.sender.getServerSession().deliver(sender, channel.getId(), data, null); // this code does not work
}
}
}
this line channel.publish(sender, data, null); // this code works fine works fine, now I don't want channel to publish message to all clients subscirbed with it, I want to send to a specific client, so I write this this.sender.getServerSession().deliver(sender, channel.getId(), data, null);, but it does not work, browser can't get message.
thx in advance.
I strongly recommend that you spend some time reading the CometD concepts page, in particular the section about sessions.
Your code does not work because you are sending the message to the sender, not to the recipient.
You need to pick which remote ServerSession you want to send the message to among the many that may be connected to your server, and call serverSession.deliver(...) on that remote ServerSession.
How to pick the remote ServerSession depends on your application.
For example:
for (ServerSession session : bayeuxServer.getSessions())
{
if (isAdminUser(session))
session.deliver(sender, channel.getId(), data, null);
}
You have to provide an implementation of isAdmin(ServerSession) with your logic, of course.
Note that you don't need to iterate over the sessions: if you happen to know the session id to deliver to, you can do:
bayeuxServer.getSession(sessionId).deliver(sender, channel.getId(), data, null);
Also refer to the CometD chat demo shipped with the CometD distribution, that contain a full fledged example of how to send a message to particular session.
I'm facing a problem while working with SmartFoxServer 2X that I want to share seeking any help/advice from the community. It might be the default behaviour of SFS, but I want to do it another way for my project. Here's the scenario:
Server Version: 2.0.0-RC1 and I'm using AS3 for client side coding. I'm not doing anything on server side and using basic/default methods of SFS in AS3.
The user logs in a particular Zone and gets a list of rooms available. All the rooms must have a maximum of 2 users. If there is no room, the user creates the room (with settings.maxUsers = 2;) and joins that room. If there is any room, the user checks for a room with room.userCount<2 and joins that room.
If all the rooms are full, the user creates a new room and joins that room, so that another user can log in and join this room. Now, when the first user logs in, a room is created and the user waits for the next user to log in and join that room.
The second user logs in and joins the room created by the first user. Now, when the third user logs in, the second user joins the newly created room while staying in the room created by the first user as well. (NOTE: Only the second user behaves like this; the first user stays in the same room. Similarly when there are 4, 5 and 6 users in the game, the 5th shares the rooms with the 4th and 6th users, in the same fashion as the second user does).
Now there are two rooms. The first room is shared by the first and second users, and the second room is shared by the second and third users. That might be the default way of SFS to handle rooms and users joining them. But I want to keep the first and second players in the first room, even on creation of a new room in the zone, and then create a new room for the third user so that the 4th user can join the room with the third user and so on.
Thanks for having a look into it. Please help.
private function onLogin(event:SFSEvent):void {
SFSVar.removeEventListener(SFSEvent.LOGIN, onLogin);
var count:int = 0;
var roomList:Array = event.currentTarget.roomList;
if(roomList.length==0) {
var my_date:Date = new Date();
var settings:RoomSettings = new RoomSettings("PoolGame"+my_date.fullYear+my_date.month+my_date.date+my_date.hours+my_date.minutes+my_date.seconds);
settings.maxUsers = 2;
settings.groupId = "default";
settings.isGame = true;
SFSVar.send(new CreateRoomRequest(settings, false));
} else if(roomList.length>0) {
for each(var room:Room in roomList) {
count++;
if(room.userCount==2) {
if(count==roomList.length) {
var my_date:Date = new Date();
var settings:RoomSettings = new RoomSettings("PoolGame"+my_date.fullYear+my_date.month+my_date.date+my_date.hours+my_date.minutes+my_date.seconds);
settings.maxUsers = 2;
settings.groupId = "default";
settings.isGame = true;
SFSVar.send(new CreateRoomRequest(settings,false));
break;
}
} else if(room.userCount==1) {
var roomRequest:JoinRoomRequest = new JoinRoomRequest(room.id,null,-1);
SFSVar.send(roomRequest);
break;
}
}
}
}
private function onRoomCreation(event:SFSEvent):void {
SFSVar.removeEventListener(SFSEvent.ROOM_ADD, onRoomCreation);
var room:Room = event.params.room;
var roomRequest:JoinRoomRequest = new JoinRoomRequest(room.id,null,-1);
SFSVar.send(roomRequest);
}
private function onRoomJoin(event:SFSEvent):void {
SFSVar.removeEventListener(SFSEvent.ROOM_JOIN, onRoomJoin);
trace("room joined!!!");
}
I'm sure the original poster figured this out already but for other people reading this, the problem is that the 2nd user still has its room_add event listener. When the 3rd user joins, a new room is created, and because both the 2nd and 3rd users still have their room_add event listeners attached, they both join the new room.
To fix it, either add a line to remove the room_add event listener to the room_join event handler, or add some checking to the room_add event handler to send a room join request only if the player isn't already in a room.
One way of creating JmDNS services is :
ServiceInfo.create(type, name, port, weight, priority, props);
where props is a Map which describes some propeties of the service. Does anybody have an example illustrating the use of theese properties, for instance how to use them in the reciever part.
I've tried :
Hashtable<String,String> settings = new Hashtable<String,String>();
settings.put("host", "hhgh");
settings.put("web_port", "hdhr");
settings.put("secure_web_port", "dfhdyhdh");
ServiceInfo info = ServiceInfo.create("_workstation._tcp.local.", "service6", 80, 0, 0, true, settings);
but, then in a machine receiving this service, what can I do to see those properties?
I would apreciate any help...
ServiceInfo info = jmDNS.getServiceInfo(serviceEvent.getType(), serviceEvent.getName());
Enumeration<String> ps = info.getPropertyNames();
while (ps.hasMoreElements()) {
String key = ps.nextElement();
String value = info.getPropertyString(key);
System.out.println(key + " " + value);
}
It has been a while since this was asked but I had the same question. One problem with the original question is that the host and ports should not be put into the text field, and in this case there should actually be two service types one secure and one insecure (or perhaps make use of subtypes).
Here is an incomplete example that gets a list of running workstation services:
ServiceInfo[] serviceInfoList = jmdns.list("_workstation._tcp.local.");
if(serviceInfoList != null) {
for (int index = 0; index < serviceInfoList.length; index++) {
int port = serviceInfoList[index].getPort();
int priority = serviceInfoList[index].getPriority();
int weight = serviceInfoList[index].getWeight();
InetAddress address = serviceInfoList[index].getInetAddresses()[0];
String someProperty = serviceInfoList[index].getPropertyString("someproperty");
// Build a UI or use some logic to decide if this service provider is the
// one you want to use based on prority, properties, etc.
...
}
}
Due to the way that JmDNS is implemented the first call to list() on a given type is slow (several seconds) but subsequent calls will be pretty fast. Providers of services can change the properties by calling info.setText(settings) and the changes will be propagated out to the listeners automatically.