Make an object ServerImpl individually for each RcfSession.

Features you would like to see in RCF.
Post Reply
acDev
Posts: 27
Joined: Tue Oct 08, 2013 3:08 pm
Location: Moscow
Contact:

Make an object ServerImpl individually for each RcfSession.

Post 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).

jarl
Posts: 238
Joined: Mon Oct 03, 2011 4:53 am
Contact:

Re: Make an object ServerImpl individually for each RcfSessi

Post 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
Kind Regards

Jarl Lindrud
Delta V Software
http://www.deltavsoft.com

acDev
Posts: 27
Joined: Tue Oct 08, 2013 3:08 pm
Location: Moscow
Contact:

Re: Make an object ServerImpl individually for each RcfSessi

Post 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.

jarl
Posts: 238
Joined: Mon Oct 03, 2011 4:53 am
Contact:

Re: Make an object ServerImpl individually for each RcfSessi

Post 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;

	// ...
}
Kind Regards

Jarl Lindrud
Delta V Software
http://www.deltavsoft.com

Post Reply