@ -0,0 +1,10 @@ | |||||
*~ | |||||
#* | |||||
*# | |||||
/src/obj | |||||
/src/MW.cpp | |||||
/include/MW.h | |||||
/src/MWSession.cpp | |||||
/include/MWSession.h | |||||
/src/BuiltinSequences.cpp | |||||
/include/BuiltinSequences.h |
@ -0,0 +1,49 @@ | |||||
# Demo Certificates | |||||
This directory contains certificates used by the clients and servers in our | |||||
sample programs. These certificates are for testing purposes only and should | |||||
**never** be used in a production environment. | |||||
As provided, the server certificates use `127.0.0.1` for the Common Name, the | |||||
IP address and DNS name. This works fine when you run the client and server on | |||||
the same host. However, if you want to run them on separate hosts, you may need | |||||
to regenerate the certificates. (This is especially true for the JavaScript | |||||
examples.) | |||||
We've included the Python script `makedemocerts.py` to simplify this task. | |||||
## Prerequisites | |||||
You'll need Python to run the script. The script also depends on a utility | |||||
package from a separate [ZeroC repository][1]. You can install this package as | |||||
follows: | |||||
``` | |||||
pip install zeroc-icecertutils | |||||
``` | |||||
## Usage | |||||
Running the script with `-h` displays the following usage information: | |||||
``` | |||||
Usage: certs/makedemocerts.py [options] | |||||
Options: | |||||
-h Show this message. | |||||
-d | --debug Debugging output. | |||||
--ip <ip> The IP address for the server certificate. | |||||
--dns <dns> The DNS name for the server certificate. | |||||
--use-dns Use the DNS name for the server certificate common | |||||
name (default is to use the IP address). | |||||
``` | |||||
The `--ip`, `--dns`, and `--use-dns` options affect the generation of the server | |||||
certificate. Without any arguments, the script prompts for the value of the IP | |||||
address and DNS name. | |||||
You can specify an alternate IP address using `--ip` and an alternate DNS name | |||||
using `--dns`. The `--use-dns` flag forces the script to use the DNS name as | |||||
the server's Common Name instead of the IP address. | |||||
[1]: https://github.com/zeroc-ice/icecertutils |
@ -0,0 +1,23 @@ | |||||
-----BEGIN CERTIFICATE----- | |||||
MIIDyTCCArGgAwIBAgIIYY3YvoWm5Q0wDQYJKoZIhvcNAQELBQAwgYsxFTATBgNV | |||||
BAMMDEljZSBEZW1vcyBDQTEMMAoGA1UECwwDSWNlMRQwEgYDVQQKDAtaZXJvQywg | |||||
SW5jLjEQMA4GA1UEBwwHSnVwaXRlcjEQMA4GA1UECAwHRmxvcmlkYTELMAkGA1UE | |||||
BhMCVVMxHTAbBgkqhkiG9w0BCQEWDmluZm9AemVyb2MuY29tMB4XDTE4MDgwNzA2 | |||||
MTk1MFoXDTIzMDgwNjA2MTk1MFowgYsxFTATBgNVBAMMDEljZSBEZW1vcyBDQTEM | |||||
MAoGA1UECwwDSWNlMRQwEgYDVQQKDAtaZXJvQywgSW5jLjEQMA4GA1UEBwwHSnVw | |||||
aXRlcjEQMA4GA1UECAwHRmxvcmlkYTELMAkGA1UEBhMCVVMxHTAbBgkqhkiG9w0B | |||||
CQEWDmluZm9AemVyb2MuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC | |||||
AQEApN5uTu4/GV2EeJqnh6VQPfJOVHZ8FBJ1GXGYtA6tA07EU7CCyDPZ3kS83J+h | |||||
n7cOit56f3exPbTK1zZiUYxjFt7ttkvHJau1z8o7eNm212kQ1jAa78bnSEWHK+3m | |||||
BVARsk0BOqG5xdTQleWKE0C4TcifceAIeoOaOY4gqHryFWmmfzQjiWmseN/LZG+z | |||||
EFjI65duSz51W/3ttb/Qs8iHomx88EyViatXAFVvVdC7t5wlat88sRpAL+zaNMhZ | |||||
fZQwmIzvCcZe7buWwEelUB7q5wFMoXhoIibZLDsOjY7nQ7lPCP3cv9V87v+r1ziM | |||||
AVX6F6+XuS8BZVFEE8BbvGx7XwIDAQABoy8wLTAMBgNVHRMEBTADAQH/MB0GA1Ud | |||||
DgQWBBQKSxy2SFmKdd72PjuZOqub4ZCFajANBgkqhkiG9w0BAQsFAAOCAQEAhGDc | |||||
qkGm7hhbXCBCK1eGLuh4t3Ktl5kRtDe/LReQu2f+HS9aGaKHOfJdaABOXaQUK8ip | |||||
jiCnqoWqton96lGZUN1AI4OrpPE9vMF/s6oJ84eEn+CyTjDFnnGX0yDuCQSRmSMs | |||||
zcwIynlnnyKls2WTJqzKG46QpdedHiMWYVNRNyQzQVgYjXEamMPXELkB6hseW34s | |||||
lnJnEE5xkxRyHhuunkWt76YyJcfUHNGzzIFgDbA/13tycGvqsPnOn27WJ654ergG | |||||
Ln7PPBm58AIb+7sDUgWL0quY/vUr7fd2gkXnrL4LOOL6DBFYH5NdHBg0MsdDB6bT | |||||
fBpVrDdZVkgcXPUWKQ== | |||||
-----END CERTIFICATE----- |
@ -0,0 +1,126 @@ | |||||
#!/usr/bin/env python | |||||
# ********************************************************************** | |||||
# | |||||
# Copyright (c) 2003-2018 ZeroC, Inc. All rights reserved. | |||||
# | |||||
# ********************************************************************** | |||||
import os, sys, socket, getopt | |||||
try: | |||||
import IceCertUtils | |||||
except Exception as ex: | |||||
print("couldn't load IceCertUtils, did you install the `zeroc-icecertutils'\n" | |||||
"package from the Python package repository?\nerror: " + str(ex)) | |||||
sys.exit(1) | |||||
def usage(): | |||||
print("Usage: " + sys.argv[0] + " [options]") | |||||
print("") | |||||
print("Options:") | |||||
print("-h Show this message.") | |||||
print("-d | --debug Debugging output.") | |||||
print("--ip <ip> The IP address for the server certificate.") | |||||
print("--dns <dns> The DNS name for the server certificate.") | |||||
print("--use-dns Use the DNS name for the server certificate common") | |||||
print(" name (default is to use the IP address)." ) | |||||
sys.exit(1) | |||||
# | |||||
# Check arguments | |||||
# | |||||
debug = False | |||||
ip = None | |||||
dns = None | |||||
usedns = False | |||||
impl = "" | |||||
try: | |||||
opts, args = getopt.getopt(sys.argv[1:], "hd", ["help", "debug", "ip=", "dns=","use-dns","impl="]) | |||||
except getopt.GetoptError as e: | |||||
print("Error %s " % e) | |||||
usage() | |||||
sys.exit(1) | |||||
for (o, a) in opts: | |||||
if o == "-h" or o == "--help": | |||||
usage() | |||||
sys.exit(0) | |||||
elif o == "-d" or o == "--debug": | |||||
debug = True | |||||
elif o == "--ip": | |||||
ip = a | |||||
elif o == "--dns": | |||||
dns = a | |||||
elif o == "--use-dns": | |||||
usedns = True | |||||
elif o == "--impl": | |||||
impl = a | |||||
def request(question, newvalue, value): | |||||
while True: | |||||
sys.stdout.write(question) | |||||
sys.stdout.flush() | |||||
input = sys.stdin.readline().strip() | |||||
if input == 'n': | |||||
sys.stdout.write(newvalue) | |||||
sys.stdout.flush() | |||||
return sys.stdin.readline().strip() | |||||
else: | |||||
return value | |||||
# | |||||
# Change to the directory where the certs files are stored | |||||
# | |||||
os.chdir(os.path.dirname(os.path.abspath(__file__))) | |||||
if not ip: | |||||
try: | |||||
ip = socket.gethostbyname(socket.gethostname()) | |||||
except: | |||||
ip = "127.0.0.1" | |||||
ip = request("The IP address used for the server certificate will be: " + ip + "\n" | |||||
"Do you want to keep this IP address? (y/n) [y]", "IP : ", ip) | |||||
if not dns: | |||||
dns = "localhost" | |||||
dns = request("The DNS name used for the server certificate will be: " + dns + "\n" | |||||
"Do you want to keep this DNS name? (y/n) [y]", "DNS : ", dns) | |||||
CertificateFactory = vars(IceCertUtils)[impl + "CertificateFactory"] | |||||
factory = CertificateFactory(debug=debug, cn="Ice Demos CA") | |||||
# | |||||
# CA certificate | |||||
# | |||||
factory.getCA().save("cacert.pem").save("cacert.der") | |||||
# Client certificate | |||||
client = factory.create("client") | |||||
client.save("client.p12") | |||||
# Server certificate | |||||
server = factory.create("server", cn = (dns if usedns else ip), ip=ip, dns=dns) | |||||
server.save("server.p12") | |||||
try: | |||||
factory.getCA().save("cacert.pem").save("cacert.jks") # Used by the Database/library demo | |||||
server.save("server.jks", caalias="cacert") | |||||
client.save("client.jks", caalias="cacert") | |||||
# Don't try to generate the BKS if the JKS generation fails | |||||
try: | |||||
server.save("server.bks", caalias="cacert") | |||||
client.save("client.bks", caalias="cacert") | |||||
except Exception as ex: | |||||
for f in ["server.bks", "client.bks"]: | |||||
if os.path.exists(f): os.remove(f) | |||||
print("warning: couldn't generate BKS certificates for Android applications:\n" + str(ex)) | |||||
print("Please fix this issue if you want to run the Android demos.") | |||||
except Exception as ex: | |||||
for f in ["server.jks", "client.jks"]: | |||||
if os.path.exists(f): os.remove(f) | |||||
print("warning: couldn't generate JKS certificates for Java applications:\n" + str(ex)) | |||||
print("Please fix this issue if you want to run the Java demos.") | |||||
factory.destroy() |
@ -0,0 +1,38 @@ | |||||
#ifndef MW_MAP_I_H | |||||
#define MW_MAP_I_H | |||||
#include <Ice/Ice.h> | |||||
#include <MW.h> | |||||
#include <set> | |||||
class MWMapCallbackAdapter | |||||
{ | |||||
public: | |||||
virtual void init(Ice::StringSeq) = 0; | |||||
virtual void join(const std::shared_ptr<MW::UserJoinedEvent>&) = 0; | |||||
virtual void leave(const std::shared_ptr<MW::UserLeftEvent>&) = 0; | |||||
virtual void send(const std::shared_ptr<MW::PositionEvent>&) = 0; | |||||
}; | |||||
class MWMap | |||||
{ | |||||
public: | |||||
MWMap(bool trace, const std::shared_ptr<Ice::Logger>& logger); | |||||
void reserve(const std::string&); | |||||
void unreserve(const std::string&); | |||||
void join(const std::string&, const std::shared_ptr<MWMapCallbackAdapter>&); | |||||
void leave(const std::string&); | |||||
long long send(const std::string&, std::string); | |||||
private: | |||||
using MWMapCallbackMap = std::map<std::string, std::shared_ptr<MWMapCallbackAdapter>>; | |||||
MWMapCallbackMap _members; | |||||
std::set<std::string> _reserved; | |||||
std::mutex _mutex; | |||||
const bool _trace; | |||||
const std::shared_ptr<Ice::Logger> _logger; | |||||
}; | |||||
#endif |
@ -0,0 +1,28 @@ | |||||
#ifndef MW_SESSION_I_H | |||||
#define MW_SESSION_I_H | |||||
#include <MWSession.h> | |||||
#include <MWMap.h> | |||||
class MWSessionI : public MW::MWSession | |||||
{ | |||||
public: | |||||
MWSessionI(const std::shared_ptr<MWMap>&, const std::string&, bool trace, const std::shared_ptr<Ice::Logger>& logger); | |||||
virtual void setCallback(std::shared_ptr<MWMap::MWMapCallbackPrx>, const Ice::Current&) override; | |||||
virtual long long send(std::string, const Ice::Current&) override; | |||||
virtual void destroy(const Ice::Current&) override; | |||||
private: | |||||
const std::shared_ptr<MWMap> _MWMap; | |||||
const std::string _name; | |||||
std::shared_ptr<MWMapCallbackAdapter> _callback; | |||||
bool _destroy = false; | |||||
std::mutex _mutex; | |||||
const bool _trace; | |||||
const std::shared_ptr<Ice::Logger> _logger; | |||||
}; | |||||
#endif |
@ -0,0 +1,25 @@ | |||||
#ifndef MW_SESSION_MANAGER_I_H | |||||
#define MW_SESSION_MANAGER_I_H | |||||
#include <Glacier2/Glacier2.h> | |||||
#include <string> | |||||
#include <MWMap.h> | |||||
class MWSessionManagerI : public Glacier2::SessionManager | |||||
{ | |||||
public: | |||||
MWSessionManagerI(const std::shared_ptr<MWMap>&, bool trace, const std::shared_ptr<Ice::Logger>& logger); | |||||
virtual std::shared_ptr<Glacier2::SessionPrx> create(std::string, | |||||
std::shared_ptr<Glacier2::SessionControlPrx>, | |||||
const Ice::Current&) override; | |||||
private: | |||||
const std::shared_ptr<MWMap> _MWMap; | |||||
const bool _trace; | |||||
const std::shared_ptr<Ice::Logger> _logger; | |||||
}; | |||||
#endif |
@ -0,0 +1,77 @@ | |||||
#pragma once | |||||
#include "Ice/BuiltinSequences.ice" | |||||
#include "Glacier2/Session.ice" | |||||
module MW | |||||
{ | |||||
/** | |||||
* | |||||
* The InvalidMessageException is raised when a user sends an invalid | |||||
* message to the server. A message is considered invalid if the | |||||
* message size exceeds the maximum message size. | |||||
* | |||||
**/ | |||||
exception InvalidMessageException | |||||
{ | |||||
/** | |||||
* | |||||
* The reason why the message was rejected by the server. | |||||
* | |||||
**/ | |||||
string reason; | |||||
} | |||||
class MWMapEvent | |||||
{ | |||||
/** The timestamp. */ | |||||
long timestamp; | |||||
/** The name of the user. */ | |||||
string name; | |||||
} | |||||
/** | |||||
* | |||||
* A sequence of state changes in the MW map. | |||||
* | |||||
* @see MWMapEvent | |||||
* | |||||
**/ | |||||
sequence<MWMapEvent> MWMapEventSeq; | |||||
/** | |||||
* | |||||
* This event is generated when a user joins the MW map. | |||||
* | |||||
* @see MWMapEvent | |||||
* | |||||
**/ | |||||
class UserJoinedEvent extends MWMapEvent | |||||
{ | |||||
} | |||||
/** | |||||
* | |||||
* This event is generated when a user leaves the MW map. | |||||
* | |||||
* @see MWMapEvent | |||||
* | |||||
**/ | |||||
class UserLeftEvent extends MWMapEvent | |||||
{ | |||||
} | |||||
/** | |||||
* | |||||
* This event is generated when a user sends a posotion in the | |||||
* map. | |||||
* | |||||
* @see MWMapEvent | |||||
* | |||||
**/ | |||||
class PositionEvent extends MWMapEvent | |||||
{ | |||||
/** The contents of the message. */ | |||||
string message; | |||||
} | |||||
} |
@ -0,0 +1,99 @@ | |||||
#include <MWMap.h> | |||||
using namespace std; | |||||
MWMap::MWMap(bool trace, const shared_ptr<Ice::Logger>& logger) : | |||||
_trace(trace), | |||||
_logger(logger) | |||||
{ | |||||
} | |||||
void | |||||
MWMap::reserve(const string& name) | |||||
{ | |||||
lock_guard<mutex> sync(_mutex); | |||||
if(_reserved.find(name) != _reserved.end() || _members.find(name) != _members.end()) | |||||
{ | |||||
throw runtime_error("The name " + name + " is already in use."); | |||||
} | |||||
_reserved.insert(name); | |||||
} | |||||
void | |||||
MWMap::unreserve(const string& name) | |||||
{ | |||||
lock_guard<mutex> sync(_mutex); | |||||
_reserved.erase(name); | |||||
} | |||||
void | |||||
MWMap::join(const string& name, const shared_ptr<MWMapCallbackAdapter>& callback) | |||||
{ | |||||
lock_guard<mutex> sync(_mutex); | |||||
long long timestamp = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count(); | |||||
_reserved.erase(name); | |||||
Ice::StringSeq names; | |||||
for(const auto& q : _members) | |||||
{ | |||||
names.push_back(q.first); | |||||
} | |||||
callback->init(move(names)); | |||||
_members[name] = callback; | |||||
auto e = make_shared<MW::UserJoinedEvent>(timestamp, name); | |||||
for(const auto& q: _members) | |||||
{ | |||||
q.second->join(e); | |||||
} | |||||
if(_trace) | |||||
{ | |||||
Ice::Trace out(_logger, "info"); | |||||
out << "User '" << name << "' joined the MW Map."; | |||||
} | |||||
} | |||||
void | |||||
MWMap::leave(const string& name) | |||||
{ | |||||
lock_guard<mutex> sync(_mutex); | |||||
long long timestamp = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count(); | |||||
_members.erase(name); | |||||
auto e = make_shared<MW::UserLeftEvent>(timestamp, name); | |||||
for(const auto& q: _members) | |||||
{ | |||||
q.second->leave(e); | |||||
} | |||||
if(_trace) | |||||
{ | |||||
Ice::Trace out(_logger, "info"); | |||||
out << "User '" << name << "' left the MWMap."; | |||||
} | |||||
} | |||||
Ice::Long | |||||
MWMap::send(const string& name, string message) | |||||
{ | |||||
lock_guard<mutex> sync(_mutex); | |||||
long long timestamp = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count(); | |||||
auto e = make_shared<MW::PositionEvent>(timestamp, name, message); | |||||
for(const auto& q: _members) | |||||
{ | |||||
q.second->send(e); | |||||
} | |||||
if(_trace) | |||||
{ | |||||
Ice::Trace out(_logger, "info"); | |||||
out << name << ": " << message; | |||||
} | |||||
return timestamp; | |||||
} |
@ -0,0 +1,84 @@ | |||||
#include <Ice/Ice.h> | |||||
#include <MWSessionManagerI.h> | |||||
// #include <PollingChatSessionFactoryI.h> | |||||
using namespace std; | |||||
class MWServer : public Ice::Service | |||||
{ | |||||
public: | |||||
virtual bool start(int argc, char* argv[], int&) override; | |||||
virtual bool stop() override; | |||||
private: | |||||
shared_ptr<Ice::ObjectAdapter> _adapter; | |||||
}; | |||||
bool | |||||
MWServer::start(int, char*[], int& status) | |||||
{ | |||||
// int timeout = communicator()->getProperties()->getPropertyAsIntWithDefault("PollingChatSessionTimeout", 10); | |||||
bool traceEnabled = communicator()->getProperties()->getPropertyAsIntWithDefault("Server.Trace", 0) != 0; | |||||
auto logger = communicator()->getLogger(); | |||||
try | |||||
{ | |||||
_adapter = communicator()->createObjectAdapter("MWServer"); | |||||
auto mwmap = make_shared<MWMap>(traceEnabled, logger); | |||||
if(traceEnabled) | |||||
{ | |||||
Ice::Trace out(logger, "info"); | |||||
out << "MW room created ok."; | |||||
} | |||||
_adapter->add(make_shared<MWSessionManagerI>(mwmap, traceEnabled, logger), | |||||
Ice::stringToIdentity("MWSessionManager")); | |||||
if(traceEnabled) | |||||
{ | |||||
Ice::Trace out(logger, "info"); | |||||
out << "MW session manager created ok."; | |||||
} | |||||
// _adapter->add(make_shared<PollingChatSessionFactoryI>(chatRoom, timeout, traceEnabled, logger), | |||||
// Ice::stringToIdentity("PollingChatSessionFactory")); | |||||
// if(traceEnabled) | |||||
// { | |||||
// Ice::Trace out(logger, "info"); | |||||
// out << "Polling chat session factory created ok."; | |||||
// } | |||||
_adapter->activate(); | |||||
if(traceEnabled) | |||||
{ | |||||
Ice::Trace out(logger, "info"); | |||||
out << "MW server started ok."; | |||||
} | |||||
} | |||||
catch(const Ice::LocalException&) | |||||
{ | |||||
status = 1; | |||||
throw; | |||||
} | |||||
status = 0; | |||||
return true; | |||||
} | |||||
bool | |||||
MWServer::stop() | |||||
{ | |||||
return true; | |||||
} | |||||
int | |||||
main(int argc, char* argv[]) | |||||
{ | |||||
#ifdef ICE_STATIC_LIBS | |||||
Ice::registerIceSSL(); | |||||
Ice::registerIceWS(); | |||||
#endif | |||||
MWServer app; | |||||
return app.main(argc, argv); | |||||
} |
@ -0,0 +1,113 @@ | |||||
#pragma once | |||||
#include <Ice/BuiltinSequences.ice> | |||||
#include <Glacier2/Session.ice> | |||||
#include <MW.ice> | |||||
module MW | |||||
{ | |||||
/** | |||||
* | |||||
* The MWRoomCallback interface is the interface that clients implement | |||||
* as their callback object. | |||||
* | |||||
* The server calls operations of this interface to communicate | |||||
* with connected clients. | |||||
* | |||||
**/ | |||||
interface MWMapCallback | |||||
{ | |||||
/** | |||||
* | |||||
* The server invokes this operation when the client sets the callback | |||||
* for a session. This provides the client with the initial list of users | |||||
* currently in the MW room. | |||||
* | |||||
* @param users The names of users currently in the MW room. | |||||
* | |||||
**/ | |||||
void init(Ice::StringSeq users); | |||||
/** | |||||
* | |||||
* The server invokes this operation to deliver a message | |||||
* that was sent to the MW room. | |||||
* | |||||
* @param name The name of the user that send the message. | |||||
* | |||||
* @param message The contents of the message. | |||||
* | |||||
* @param timestamp The time at which the message was sent. | |||||
* | |||||
**/ | |||||
void send(long timestamp, string name, string message); | |||||
/** | |||||
* | |||||
* The server invokes this operation when a user joins | |||||
* the MW room. | |||||
* | |||||
* @param name The name of the user that joined the MW room. | |||||
* | |||||
* @param timestamp The time at which the user joined the MW room. | |||||
* | |||||
**/ | |||||
void join(long timestamp, string name); | |||||
/** | |||||
* | |||||
* The servers invokes this operation when a user leaves | |||||
* the MW room. | |||||
* | |||||
* @param name The name of the user that left the MW room. | |||||
* | |||||
* @param timestamp The time at which the user left the MW room. | |||||
* | |||||
**/ | |||||
void leave(long timestamp, string name); | |||||
} | |||||
/** | |||||
* | |||||
* A MWSession is a custom Glacier2::Session for clients that use | |||||
* Glacier2 and support callbacks (such as C++, C# and clients). | |||||
* | |||||
* @see Glacier2::Session | |||||
* | |||||
**/ | |||||
interface MWSession extends Glacier2::Session | |||||
{ | |||||
/** | |||||
* | |||||
* The setCallback operation is called by clients to set the | |||||
* callback used to receive notification of activity in the | |||||
* room. Clients receive notifications as soon as they call this | |||||
* operation (before setCallback returns). | |||||
* | |||||
* The first callback made by the server is a call to | |||||
* MWRoomCallback::init, which delivers the current list of | |||||
* users to the client. | |||||
* | |||||
* @param cb The callback the server uses to deliver notifications. | |||||
* | |||||
* @see MWRoomCallback | |||||
* | |||||
**/ | |||||
void setCallback(MWMapCallback* cb); | |||||
/** | |||||
* | |||||
* Send a message to the MW room. | |||||
* | |||||
* @param message The message to be sent. | |||||
* | |||||
* @return The time at which the message is sent. | |||||
* | |||||
* @throws InvalidMessageException should the message be invalid. | |||||
* | |||||
**/ | |||||
long send(string message) throws InvalidMessageException; | |||||
} | |||||
} |
@ -0,0 +1,210 @@ | |||||
#include <MWSessionI.h> | |||||
#include <MWUtils.h> | |||||
using namespace std; | |||||
class SessionCallbackAdapter : public MWMapCallbackAdapter, | |||||
public enable_shared_from_this<SessionCallbackAdapter> | |||||
{ | |||||
public: | |||||
SessionCallbackAdapter(const shared_ptr<MW::MWRoomCallbackPrx>& callback, | |||||
const shared_ptr<MW::MWSessionPrx>& session, | |||||
bool trace, const shared_ptr<Ice::Logger>& logger, const std::string& name) : | |||||
_callback(callback), | |||||
_session(session), | |||||
_trace(trace), | |||||
_logger(logger), | |||||
_name(name) | |||||
{ | |||||
} | |||||
void init(Ice::StringSeq users) override | |||||
{ | |||||
auto self = shared_from_this(); | |||||
try | |||||
{ | |||||
_callback->initAsync(users, nullptr, [self](std::exception_ptr eptr) { self->failed(eptr); }); | |||||
} | |||||
catch(const Ice::CommunicatorDestroyedException&) | |||||
{ | |||||
// Ignored server is being shutdown | |||||
} | |||||
} | |||||
void join(const shared_ptr<MW::UserJoinedEvent>& e) override | |||||
{ | |||||
auto self = shared_from_this(); | |||||
try | |||||
{ | |||||
_callback->joinAsync(e->timestamp, e->name, nullptr, [self](exception_ptr eptr) { self->failed(eptr); }); | |||||
} | |||||
catch(const Ice::CommunicatorDestroyedException&) | |||||
{ | |||||
// Ignored server is being shutdown | |||||
} | |||||
} | |||||
void leave(const shared_ptr<MW::UserLeftEvent>& e) override | |||||
{ | |||||
auto self = shared_from_this(); | |||||
try | |||||
{ | |||||
_callback->leaveAsync(e->timestamp, e->name, nullptr, [self](exception_ptr eptr) { self->failed(eptr); }); | |||||
} | |||||
catch(const Ice::CommunicatorDestroyedException&) | |||||
{ | |||||
// Ignored server is being shutdown | |||||
} | |||||
} | |||||
void send(const shared_ptr<MW::MessageEvent>& e) override | |||||
{ | |||||
auto self = shared_from_this(); | |||||
try | |||||
{ | |||||
_callback->sendAsync(e->timestamp, e->name, e->message, nullptr, [self](exception_ptr eptr) { self->failed(eptr); }); | |||||
} | |||||
catch(const Ice::CommunicatorDestroyedException&) | |||||
{ | |||||
// Ignored server is being shutdown | |||||
} | |||||
} | |||||
void failed(exception_ptr) | |||||
{ | |||||
if(_trace) | |||||
{ | |||||
Ice::Trace out(_logger, "info"); | |||||
out << "Error sending request to user '" << _name << "'. The user's session will be destroyed."; | |||||
} | |||||
try | |||||
{ | |||||
_session->ice_endpoints(Ice::EndpointSeq())->destroy(); // Collocated call. | |||||
} | |||||
catch(const Ice::LocalException&) | |||||
{ | |||||
} | |||||
} | |||||
private: | |||||
const shared_ptr<MW::MWMapCallbackPrx> _callback; | |||||
const shared_ptr<MW::MWSessionPrx> _session; | |||||
const bool _trace; | |||||
const shared_ptr<Ice::Logger> _logger; | |||||
const string _name; | |||||
}; | |||||
MWSessionI::MWSessionI(const shared_ptr<MWMap>& MWMap, const string& name, bool trace, const shared_ptr<Ice::Logger>& logger) : | |||||
_MWMap(MWMap), | |||||
_name(name), | |||||
_trace(trace), | |||||
_logger(logger) | |||||
{ | |||||
} | |||||
void | |||||
MWSessionI::setCallback(shared_ptr<MW::MWMapCallbackPrx> callback, const Ice::Current& current) | |||||
{ | |||||
lock_guard<mutex> sync(_mutex); | |||||
if(_destroy) | |||||
{ | |||||
if(_trace) | |||||
{ | |||||
Ice::Trace out(_logger, "info"); | |||||
out << "User '" << _name << "' tried to set the session callback but the session is already destroyed."; | |||||
} | |||||
throw Ice::ObjectNotExistException(__FILE__, __LINE__); | |||||
} | |||||
if(_callback || !callback) | |||||
{ | |||||
return; | |||||
} | |||||
Ice::Context ctx; | |||||
ctx["_fwd"] = "o"; | |||||
_callback = make_shared<SessionCallbackAdapter>( | |||||
callback->ice_context(ctx), | |||||
Ice::uncheckedCast<MW::MWSessionPrx>(current.adapter->createProxy(current.id)), | |||||
_trace, _logger, _name); | |||||
_MWRoom->join(_name, _callback); | |||||
} | |||||
long long | |||||
MWSessionI::send(string message, const Ice::Current&) | |||||
{ | |||||
lock_guard<mutex> sync(_mutex); | |||||
if(_destroy) | |||||
{ | |||||
if(_trace) | |||||
{ | |||||
Ice::Trace out(_logger, "info"); | |||||
out << "User '" << _name << "' tried to send a message but the session is already destroyed."; | |||||
} | |||||
throw Ice::ObjectNotExistException(__FILE__, __LINE__); | |||||
} | |||||
if(!_callback) | |||||
{ | |||||
if(_trace) | |||||
{ | |||||
Ice::Trace out(_logger, "info"); | |||||
out << "User '" << _name << "' tried to send a message without setting the callback."; | |||||
} | |||||
throw MW::InvalidMessageException("You cannot send messages until you join the MW Map."); | |||||
} | |||||
string msg; | |||||
try | |||||
{ | |||||
msg = validateMessage(message); | |||||
} | |||||
catch(const exception& ex) | |||||
{ | |||||
if(_trace) | |||||
{ | |||||
Ice::Trace out(_logger, "info"); | |||||
out << "User '" << _name << "' sent an invalid message:\n" << ex; | |||||
} | |||||
throw MW::InvalidMessageException(ex.what()); | |||||
} | |||||
return _MWMap->send(_name, move(msg)); | |||||
} | |||||
void | |||||
MWSessionI::destroy(const Ice::Current& current) | |||||
{ | |||||
lock_guard<mutex> sync(_mutex); | |||||
if(_destroy) | |||||
{ | |||||
if(_trace) | |||||
{ | |||||
Ice::Trace out(_logger, "info"); | |||||
out << "Trying to destroy the session for user '" << _name << "' but the session is already destroyed."; | |||||
} | |||||
throw Ice::ObjectNotExistException(__FILE__, __LINE__); | |||||
} | |||||
try | |||||
{ | |||||
current.adapter->remove(current.id); | |||||
if(_callback) | |||||
{ | |||||
_MWMap->leave(_name); | |||||
} | |||||
else | |||||
{ | |||||
_MWMap->unreserve(_name); | |||||
} | |||||
} | |||||
catch(const Ice::ObjectAdapterDeactivatedException&) | |||||
{ | |||||
// No need to clean up, the server is shutting down. | |||||
} | |||||
if(_trace) | |||||
{ | |||||
Ice::Trace out(_logger, "info"); | |||||
out << "Push session for user '" << _name << "' destroyed."; | |||||
} | |||||
_destroy = true; | |||||
} |
@ -0,0 +1,66 @@ | |||||
#include <ChatSessionManagerI.h> | |||||
#include <ChatSessionI.h> | |||||
#include <ChatUtils.h> | |||||
using namespace std; | |||||
ChatSessionManagerI::ChatSessionManagerI(const shared_ptr<ChatRoom>& chatRoom, bool trace, | |||||
const shared_ptr<Ice::Logger>& logger) : | |||||
_chatRoom(chatRoom), | |||||
_trace(trace), | |||||
_logger(logger) | |||||
{ | |||||
} | |||||
shared_ptr<Glacier2::SessionPrx> | |||||
ChatSessionManagerI::create(string name, | |||||
shared_ptr<Glacier2::SessionControlPrx> sessionControl, | |||||
const Ice::Current& current) | |||||
{ | |||||
string vname; | |||||
try | |||||
{ | |||||
vname = validateName(name); | |||||
_chatRoom->reserve(vname); | |||||
} | |||||
catch(const exception& ex) | |||||
{ | |||||
if(_trace) | |||||
{ | |||||
Ice::Trace out(_logger, "info"); | |||||
out << "Cannot create push session:\n" << ex; | |||||
} | |||||
throw Glacier2::CannotCreateSessionException(ex.what()); | |||||
} | |||||
shared_ptr<Glacier2::SessionPrx> proxy; | |||||
try | |||||
{ | |||||
auto session = make_shared<ChatSessionI>(_chatRoom, vname, _trace, _logger); | |||||
proxy = Ice::uncheckedCast<Glacier2::SessionPrx>(current.adapter->addWithUUID(session)); | |||||
Ice::IdentitySeq ids; | |||||
ids.push_back(proxy->ice_getIdentity()); | |||||
sessionControl->identities()->add(ids); | |||||
} | |||||
catch(const Ice::LocalException& ex) | |||||
{ | |||||
if(_trace) | |||||
{ | |||||
Ice::Trace out(_logger, "info"); | |||||
out << "Cannot create push session for user '" << vname << "':\n" << ex; | |||||
} | |||||
if(proxy) | |||||
{ | |||||
proxy->destroy(); | |||||
} | |||||
throw Glacier2::CannotCreateSessionException("internal server error"); | |||||
} | |||||
if(_trace) | |||||
{ | |||||
Ice::Trace out(_logger, "info"); | |||||
out << "Push session created for user '" << vname << "'."; | |||||
} | |||||
return proxy; | |||||
} |
@ -0,0 +1,29 @@ | |||||
CC=c++ | |||||
IDIR =../include | |||||
CFLAGS=-I. -DICE_CPP11_MAPPING -I $(IDIR) -std=c++11 | |||||
ODIR=obj | |||||
LDIR =../lib | |||||
LIBS=-lm -lIce++11 | |||||
_DEPS = MW.h MWMap.h MWSession.h MWSessionI.h MWSessionManagerI.h | |||||
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS)) | |||||
_OBJ = MW.o MWServer.o MWMap.o MWSession.o MWSesionManagerI.o MWUtils.o | |||||
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ)) | |||||
$(ODIR)/%.o: %.cpp $(DEPS) | |||||
$(CC) -c -o $@ $< $(CFLAGS) | |||||
slice: | |||||
slice2cpp -I. -I../../ice/slice *.ice | |||||
mv *.h ../include | |||||
mwserver: $(OBJ) | |||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS) | |||||
.PHONY: clean | |||||
clean: | |||||
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~ |
@ -1,7 +0,0 @@ | |||||
module Demo | |||||
{ | |||||
interface Printer | |||||
{ | |||||
void printString(string s); | |||||
} | |||||
} |
@ -1,38 +0,0 @@ | |||||
#include <Ice/Ice.h> | |||||
#include <Printer.h> | |||||
using namespace std; | |||||
using namespace Demo; | |||||
class PrinterI : public Printer | |||||
{ | |||||
public: | |||||
virtual void printString(string s, const Ice::Current&) override; | |||||
}; | |||||
void | |||||
PrinterI::printString(string s, const Ice::Current&) | |||||
{ | |||||
cout << s << endl; | |||||
} | |||||
int | |||||
main(int argc, char* argv[]) | |||||
{ | |||||
try | |||||
{ | |||||
Ice::CommunicatorHolder ich(argc, argv); | |||||
// Server implementation here ... | |||||
auto adapter = ich->createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000"); | |||||
auto servant = make_shared<PrinterI>(); | |||||
adapter->add(servant, ich->stringToIdentity("SimplePrinter")); | |||||
adapter->activate(); | |||||
ich->waitForShutdown(); | |||||
} | |||||
catch(const std::exception& e) | |||||
{ | |||||
cerr << e.what() << endl; | |||||
return 1; | |||||
} | |||||
return 0; | |||||
} |
@ -0,0 +1,84 @@ | |||||
# | |||||
# Set the Glacier2 instance name. | |||||
# | |||||
Glacier2.InstanceName=MWServer | |||||
# | |||||
# The client-visible endpoint of Glacier2. This should be an endpoint | |||||
# visible from the public Internet. | |||||
# | |||||
Glacier2.Client.Endpoints=ssl -p 4064 -t 10000 -h 127.0.0.1:tcp -p 4502 -t 10000 -h 127.0.0.1 | |||||
# | |||||
# The server-visible endpoint of Glacier2. This endpoint is only | |||||
# required if callbacks are needed (leave empty otherwise). This | |||||
# should be an endpoint on an internal network (like 192.168.x.x), or | |||||
# on the loopback, so that the server is not directly accessible from | |||||
# the Internet. | |||||
# | |||||
Glacier2.Server.Endpoints=tcp -h 127.0.0.1 | |||||
# | |||||
# The proxy of the session manager. | |||||
# | |||||
Glacier2.SessionManager=ChatSessionManager:tcp -h 127.0.0.1 -p 10001 | |||||
# | |||||
# Accept only requests to the machine where the session manager is | |||||
# running. | |||||
# | |||||
Glacier2.Filter.Address.Accept=127.0.0.1:10001 | |||||
# | |||||
# For this demo, we use the null permissions verifier. This permissions | |||||
# verifier allows any user-id / password combination. | |||||
# | |||||
Glacier2.PermissionsVerifier=MWServer/NullPermissionsVerifier | |||||
# | |||||
# The timeout for inactive sessions. If any client session is inactive | |||||
# for longer than this value, the session expires and is removed. The | |||||
# unit is seconds. | |||||
# | |||||
Glacier2.SessionTimeout=30 | |||||
# | |||||
# Turn off buffering, it's not useful for the chat demo. | |||||
# | |||||
Glacier2.Server.Buffered=0 | |||||
Glacier2.Client.Buffered=0 | |||||
# | |||||
# Security Tracing | |||||
# | |||||
# 0 = no security tracing | |||||
# 1 = trace messages | |||||
# | |||||
#IceSSL.Trace.Security=1 | |||||
# | |||||
# SSL Configuration | |||||
# | |||||
Ice.Plugin.IceSSL=IceSSL:createIceSSL | |||||
IceSSL.DefaultDir=../certs | |||||
IceSSL.CAs=cacert.pem | |||||
IceSSL.CertFile=server.p12 | |||||
IceSSL.Password=password | |||||
IceSSL.Keychain=../../../certs/glacier2.keychain | |||||
IceSSL.KeychainPassword=password | |||||
IceSSL.VerifyPeer=0 | |||||
# | |||||
# Ice Tracing | |||||
# | |||||
#Ice.Trace.Network=1 | |||||
#Ice.Warn.Connections=1 | |||||
#Ice.Trace.Protocol=1 | |||||
# | |||||
# We configure the server thread pool as we want the glacier2router | |||||
# to be multi threaded. | |||||
# | |||||
Ice.ThreadPool.Server.Size=4 | |||||
Ice.ThreadPool.Server.SizeMax=10 |
@ -0,0 +1,44 @@ | |||||
# | |||||
# The endpoint of the session server's object adapter. This should be | |||||
# an endpoint on an internal network (like 192.168.x.x), or on the | |||||
# loopback, so that the session server is not directly accessible from | |||||
# the Internet. | |||||
# | |||||
ChatServer.Endpoints=tcp -h 127.0.0.1 -p 10001 | |||||
# | |||||
# Warn about connection exceptions | |||||
# | |||||
#Ice.Warn.Connections=1 | |||||
# | |||||
# Network Tracing | |||||
# | |||||
# 0 = no network tracing | |||||
# 1 = trace connection establishment and closure | |||||
# 2 = like 1, but more detailed | |||||
# 3 = like 2, but also trace data transfer | |||||
# | |||||
#Ice.Trace.Network=3 | |||||
# | |||||
# Protocol Tracing | |||||
# | |||||
# 0 = no protocol tracing | |||||
# 1 = trace protocol messages | |||||
# | |||||
#Ice.Trace.Protocol=1 | |||||
# | |||||
# Chat Server Tracing | |||||
# | |||||
# 0 = disable chat server tracing | |||||
# 1 = enable chat server tracing | |||||
Server.Trace=1 | |||||
# | |||||
# We configure the server thread pool as we want the chatserver | |||||
# to be multi threaded. | |||||
# | |||||
Ice.ThreadPool.Server.Size=4 | |||||
Ice.ThreadPool.Server.SizeMax=10 |