@ -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 |