Page 1 of 1
Make an object ServerImpl individually for each RcfSession.
Posted: Thu Oct 10, 2013 8:05 am
by acDev
Now we have to create an object
ServerImpl globally (or on a stack).
Code: Select all
class ServerImpl
{
public:
void Print(const std::string & s)
{
std::cout << "I_Server service: " << s << std::endl;
}
};
int main()
{
...
ServerImpl g_srv;
server.bind<I_Server>(g_srv);
...
return 0;
}
I would like to object
ServerImpl was created in conjunction with the creation of the object
RcfSession.
I would also like to see available
RcfSession object in the object
ServerImpl (as a field).
Re: Make an object ServerImpl individually for each RcfSessi
Posted: Thu Oct 10, 2013 8:46 am
by jarl
You can get the RcfSession by calling RCF::getCurrentRcfSession() from within the server implementation. To create a session object of a custom type, let's say MyServerSession, you can use RcfSession::getSessionObject(). Here is an example:
Code: Select all
// Get current RCF session.
RCF::RcfSession & rcfSession = RCF::getCurrentRcfSession();
// Create a MyServerSession for this RCF session, if it has not already been created.
MyServerSession & mySession = rcfSession.getSessionObject<MyServerSession>(true);
The ServerSession object is created the first time you call getSessionObject<ServerSession>(), and it is destroyed when the RcfSession is destroyed, which happens when the connection is disconnected.
You can read more about server sessions in the User Guide:
http://www.deltavsoft.com/doc/rcf_user_ ... r_sessions
http://www.deltavsoft.com/doc/rcf_user_ ... e.Sessions
Re: Make an object ServerImpl individually for each RcfSessi
Posted: Thu Oct 10, 2013 9:48 am
by acDev
jarl wrote:You can get the RcfSession by calling RCF::getCurrentRcfSession() from within the server implementation. To create a session object of a custom type, let's say MyServerSession, you can use RcfSession::getSessionObject().
Now I am doing just that:
Code: Select all
#define GET_RCFSES_AND_CLIENT \
RCF::RcfSession & rcfses = RCF::getCurrentRcfSession(); \
ClientData * client = rcfses.querySessionObject<ClientData>(); \
SQLite::Database * sqldb = client->getDB();
#define RCFLOG3() UTIL_LOG(2, RCF::LogLevel_3) << "[XX] "
#define RCFLOG3S() UTIL_LOG(2, RCF::LogLevel_3) << client->m_sesIdLog
bool ClientData::setSessionData(RCF::RcfSession & session)
{
ClientData * client = this;
if (m_RemoteAddr.empty()) {
m_RemoteAddr = session.getClientAddress().string();
g_sesCount++;
m_sesId = g_sesCount;
sprintf(m_sesIdLog, "[%04x] ", m_sesId);
RCFLOG3() << "Create session. RemoteAddr = " << m_RemoteAddr;
return true;
}
return false;
}
bool OnClientAccess(int fnId)
{
RCF::RcfSession & rcfses = RCF::getCurrentRcfSession();
ClientData & clientObj = rcfses.getSessionObject<ClientData>(true);
clientObj.setSessionData(rcfses);
ClientData * client = rcfses.querySessionObject<ClientData>();
SQLite::Database & sesdbObj = rcfses.getSessionObject<SQLite::Database>(true);
client->m_db = &sesdbObj;
SQLite::Database * sesdb = client->getDB();
int aFlags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX;
sesdb->open(GetSrvDatabaseName().c_str(), aFlags);
if (!sesdb->is_open()) {
throw std::runtime_error("Can't open database");
}
sesdb->setBusyTimeout(2000);
if (fnId == 0) {
// Login method !!!
return true;
}
if (!clientObj.m_Logged) {
std::string sTokenId = rcfses.getRequestUserData();
int hr = CheckTokenId(rcfses, client, sesdb, sTokenId);
RCFLOG3() << "CheckTokenId: TokenId = '" << sTokenId << "' (result = 0x" << std::hex << hr << ")";
if (hr == S_OK) clientObj.m_Logged = true;
}
return clientObj.m_Logged;
}
int ServerImpl::Print(const std::string & text)
{
GET_RCFSES_AND_CLIENT
RCFLOG3() << "'Print' service: " << text;
SQLite::Statement st(*sqldb, "INSERT INTO table_text (text) VALUES (:text)");
st.bind(":text", text);
int rows = st.exec();
if (rows != 1) return S_FALSE;
return S_OK;
}
I see that the code is just horrible! (see on GET_RCFSES_AND_CLIENT macro)
Initially, class
SQLite::Database does not have a default-constructor. I had to edit the source code of "SQLiteC++" library.
I think that field
RCF::RcfSession * rcfses ,
ClientData * client ,
SQLite::Database * sqldb should be in the class
ServerImpl.
Re: Make an object ServerImpl individually for each RcfSessi
Posted: Fri Oct 11, 2013 3:38 am
by jarl
It's definitely a good idea to avoid macros. I think the easiest way to write the code is like I've done below. I've made the SQLite database part of the ClientData class, so you can construct it yourself with the relevant arguments, instead of adding a default constructor.
Code: Select all
class ClientData
{
public:
ClientData();
void initialize();
// ...
private:
bool mInit;
boost::shared_ptr<SQLite::Database> mSessionDbPtr;
};
ClientData::ClientData() : mInit(false)
{
}
void ClientData::initialize()
{
if (!mInit)
{
// Log client connection info.
RCF::RcfSession & rcfSession = RCF::getCurrentRcfSession();
m_RemoteAddr = pSession->getClientAddress().string();
g_sesCount++;
m_sesId = g_sesCount;
sprintf(m_sesIdLog, "[%04x] ", m_sesId);
RCFLOG3() << "Create session. RemoteAddr = " << m_RemoteAddr;
// Setup database connection.
mSessionDbPtr.reset( new SQLite::Database( /*arguments here*/) );
int aFlags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX;
mSessionDbPtr->open(GetSrvDatabaseName().c_str(), aFlags);
if (!mSessionDbPtr->is_open()) {
throw std::runtime_error("Can't open database");
}
mSessionDbPtr->setBusyTimeout(2000);
// Check token.
if (!clientData.m_Logged)
{
std::string sTokenId = rcfSession.getRequestUserData();
int hr = CheckTokenId(rcfSession, client, sesdb, sTokenId);
RCFLOG3() << "CheckTokenId: TokenId = '" << sTokenId << "' (result = 0x" << std::hex << hr << ")";
if (hr == S_OK) clientObj.m_Logged = true;
}
mInit = true;
}
}
// The purpose of this function is to ensure that Login() is called before any other remote method.
bool OnClientAccess(int fnId)
{
// Login() has fnId of zero.
if (fnId != 0)
{
RCF::RcfSession & rcfSession = RCF::getCurrentRcfSession();
ClientData * pClientData = rcfSession.querySessionObject<ClientData>(true);
if (!pClientData)
{
throw std::runtime_error("Error: client must call Login() before any other remote method.");
}
}
}
int ServerImpl::Login()
{
RCF::RcfSession & rcfSession = RCF::getCurrentRcfSession();
ClientData & clientData = rcfSession.getSessionObject<ClientData>(true);
clientData.initialize();
}
int ServerImpl::Print(const std::string & text)
{
RCF::RcfSession & rcfSession = RCF::getCurrentRcfSession();
ClientData & clientData = rcfSession.getSessionObject<ClientData>(true);
SQLite::Database & sqldb = * clientData.mSessionDbPtr;
// ...
}