Symbian Active Object Framework - A Classic Example for Open Closed Principle

in
Keywords:

Symbian Active Object Framework - A Classic Example for Open Closed Principle

While doing Object Oriented Design (OOD), the fundamental Design Principle that comes first is Open Closed Principle (OCP). Bertrand Meyer is the originater of this principle. One line meaning for this design principle is, "A module should be open for extension but closed for modification". Or to put it in other words, "we should design our Module such a way that, they can be extendable without requiring them to be modified later for some more requirement". Or we can put it like this also, "we should be able to change the behaviour of the module, with out changing (or touching) the existing source code of the module".

Eventhough these statements seem to be contradictory and looks impossible, there are several ways of achieving the OCP for some extend. Achieving OCP completely may not be possible. Fundamental aspect for all these techniques for OCP is based upon ABSTRACTION. It can be implemented by making use of: 1. Dynamic Polymorphism 2. Static Polymorphism

Introduction:

While Designing Symbian, they (Designer of Symbian OS) have extensively made use of OCP for their Design, so that the User of Symbian can make use of Symbian Framework for most of their purpose, just by extending the existing Framework, which made programmer life very easy.

One of the classic examples for this is Active Object (and Active Scheduler) Framework provided by Symbian

The abstraction of core class of the active object (Cactive) looks like this:

class CActive : public CBase
{
public:
   IMPORT_C virtual ~CActive();
   IMPORT_C void Cancel();
   inline TBool IsActive() const;
   inline TInt Priority() const;
protected:
   IMPORT_C CActive(TInt aPriority);
   IMPORT_C void SetActive();

   // Pure virtual functions
   
   /*
     Implements cancellation of  outstanding request.
     This function is called as part of the active        
     object's Cancel().
   */
   virtual void DoCancel() =0;

   /*
     Handles an active object's request completion event.
     A derived class must provide an implementation to handle the completed request.
     The function is called by the active scheduler when a request completion event occurs.
    */
    virtual void RunL() =0;

public:
    //The request status associated with an asynchronous request.
    TRequestStatus iStatus;

private:
    TBool iActive;
    TPriQueLink iLink;
    friend class CActiveScheduler;
};

It encapsulates both the issuing of a request to an asynchronous service provider and the handling of completed requests.

In the abstraction notice that, two APIs are made as pure virtual, so that the User of Active Objects (CActive) has to implement their own Active Object inheriting from CActive and have to give implementation to RunL() and DoCancel(). The Active Scheduler will call RunL, when there is a outstanding request pending from that active object and there are no other active objects that are of higher priority. User Specific behavior will be implemented in RunL of Active Object.

DoCancel will be called internally by the Cancel (CActive::Cancel will call DoCancel of derived class) function, where user will give implementation for canceling the outstanding request and does the resource specific cancel operation for all User Specific resources.

So, it's the way of changing the behiour of the existing Framework with out changing the existing framework. We can have as many as Active objects with different behiour as per our requirement.

Framework Summary:

Figure-1-FrameworkSummery.jpg

As you can see in the figure above(Figuere 1 in the attached document), an Active Scheduler will have one or more Active Objects which will be Serviced depending on their priority and request. CActive is an abstract class and user active objects must be inherited by this one and will be added to Active Scheduler using CActiveScheduler::Add(CActive* ) API. There will be only one instance of Active Scheduler per Thread that will handle all the Active Objects of that Thread. One can start a scheduler using CActiveScheduler::Start() API.

Example Implementation

#ifndef MYACTIVEOBJECT_H
#define MYACTIVEOBJECT_H

#include <e32base.h>

class CMyActiveObject : public CActive
{
public:
 enum TRequestType {ECount, EStop} ;

public:
 CMyActiveObject( TRequestType aRequest = ECount, TInt aPriority = EPriorityStandard );
 void DoCancel();
 void RunL();
 void RequestFunction( TRequestType aRequest );
 void ReadyToGo();
 void SetIncrementCounter( TInt aCount = 1 );
 TUint CountValue () const;
 CMyActiveObject();
private:
 TUint iCount;
 TUint iIncrementCount;
 TRequestType iRequest;
} ;

#endif //MYACTIVEOBJECT_H

#include "MyActiveObject.h"

// -----------------------------------------------------------------------------
// CMyActiveObject::CMyActiveObject
// Description: CMyActiveObject constructer
// -----------------------------------------------------------------------------
//
CMyActiveObject::CMyActiveObject( TRequestType aRequest, TInt aPriority ) : CActive( aPriority ), iCount( 0 ), iIncrementCount( 1 ), iRequest( aRequest )
{
 CActiveScheduler::Add( this );
}

//
//-----------------------------------------------------------------------------
// CMyActiveObject:: CMyActiveObject
// Description: CMyActiveObject Destructer
// -----------------------------------------------------------------------------
//
CMyActiveObject::~CMyActiveObject()
{
 Cancel();
}

//
//-----------------------------------------------------------------------------
// CMyActiveObject::RequestFunction
// Description: Request Function for CMyActiveObject
// ----------------------------------------------------------------------------
//
void CMyActiveObject::RequestFunction( TRequestType aRequest )
{
 iRequest = aRequest;
 iStatus = KRequestPending;
 if( !IsActive() )
    SetActive();
}

//
//-----------------------------------------------------------------------------
// CMyActiveObject::ReadyToGo
// Description: This calls CMyActiveObject's RunL
//-----------------------------------------------------------------------------
//
void CMyActiveObject::ReadyToGo()
{
 TRequestStatus* status = &iStatus;
 User::RequestComplete( status, KErrNone );
}


//
//-----------------------------------------------------------------------------
// CMyActiveObject::DoCancel
// Description: This calls CActive's Cancel
//-----------------------------------------------------------------------------
//
void CMyActiveObject::DoCancel()
{
}

//
//-----------------------------------------------------------------------------
// CMyActiveObject::SetIncrementCounter
// Description: Sets Increment Counter of CMyActiveObject
//-----------------------------------------------------------------------------
//
void CMyActiveObject::SetIncrementCounter( TInt aCount)
{
iIncrementCount = aCount;
}


//
//-----------------------------------------------------------------------------
// CMyActiveObject::RunL
// Description: CActive::RunL implementation which will either stop the
// Scheduler or increment the count
//-----------------------------------------------------------------------------
//
void CMyActiveObject::RunL()
{
 switch(iRequest)
 {
   case ECount:
     iCount += iIncrementCount;
     break;
   case EStop:
     CActiveScheduler::Stop();
     break;
   default:
     break;
 }
}


//
//-----------------------------------------------------------------------------
// CMyActiveObject::CountValue
// Description: Returns Count Value of CMyActiveObject
//-----------------------------------------------------------------------------
//
TUint CMyActiveObject::CountValue () const
{
return iCount;
}

//======================================================= //Usage... SomeOtherCpp.cpp

#include "MyActiveObject.h"

const TInt KErrActiveObjCount = -1001;

TInt UseMyActiveObjectL( TUint aCount )
{
 TInt retVal ( KErrNone );
 
 //Create one Active Object for Count purpose CMyActiveObject*    
 object1 = new( ELeave ) CMyActiveObject;  
 object1->SetIncrementCounter( aCount);  
 object1->RequestFunction( CMyActiveObject::ECount );  
 CleanupStack::PushL( object1 );

 //Create one Active Object for Stopping Scheduler  
 CMyActiveObject* object2 = new( ELeave ) CMyActiveObject;
 object2->RequestFunction( CMyActiveObject::EStop ); //Issue the Request
 object1->ReadyToGo();
 object2->ReadyToGo();

 //Start the Scheduler
 CActiveScheduler::Start();
if( object1->CountValue() != aCount )
  retVal = KErrActiveObjCount;
delete object2;
CleanupStack::PopAndDestroy( object1 );
return retVal;  
}


> Symbian Active Object Framework - A Classic Example for Open C

hi, The article is informative one.Iam a novoice user of symbian,and Iam developing an application which involves active object,I tryed but Iam not getting the desired output.Can you send an example (if possible both header and cpp) which includes use of Active Objects

Thanks

> Symbian Active Object Framework - A Classic Example for Open C

Hi,

I could not get any way of attaching files with this thread. So, I am adding code with this only. May be you can mail me at Girish.Shetty@nokia.com if in case you are still not sure about the implementation and usage of Active Object so that I can mail you the code..

I am copy-pasting the working code directly, so formatting of the text/code may look very bad in this!

Here it goes:

=======================================================

//In Header File: MyActiveObject.h

#ifndef MYACTIVEOBJECT_H #define MYACTIVEOBJECT_H

#include <e32base.h>

class CMyActiveObject : public CActive public: enum TRequestType ECount, EStop ; public: CMyActiveObject( TRequestType aRequest = ECount, TInt aPriority = EPriorityStandard ); void DoCancel(); void RunL(); void RequestFunction( TRequestType aRequest ); void ReadyToGo(); void SetIncrementCounter( TInt aCount = 1 ); TUint CountValue () const; ~CMyActiveObject(); private: TUint iCount; TUint iIncrementCount; TRequestType iRequest; ;

#endif //MYACTIVEOBJECT_H

=======================================================

//Corresponding .cpp file //MyActiveObject.cpp

#include "MyActiveObject.h"

// ----------------------------------------------------------------------------- // CMyActiveObject::CMyActiveObject // Description: CMyActiveObject constructer // ----------------------------------------------------------------------------- // CMyActiveObject::CMyActiveObject( TRequestType aRequest, TInt aPriority ) : CActive( aPriority ), iCount( 0 ), iIncrementCount( 1 ), iRequest( aRequest ) CActiveScheduler::Add( this ); // ----------------------------------------------------------------------------- // CMyActiveObject::~CMyActiveObject // Description: CMyActiveObject Destructer // ----------------------------------------------------------------------------- // CMyActiveObject::~CMyActiveObject() Cancel();

// ----------------------------------------------------------------------------- // CMyActiveObject::RequestFunction // Description: Request Function for CMyActiveObject // ----------------------------------------------------------------------------- // void CMyActiveObject::RequestFunction( TRequestType aRequest ) iRequest = aRequest; iStatus = KRequestPending; if( !IsActive() ) SetActive();

// ----------------------------------------------------------------------------- // CMyActiveObject::ReadyToGo // Description: This calls CMyActiveObject's RunL // ----------------------------------------------------------------------------- // void CMyActiveObject::ReadyToGo() TRequestStatus* status = &iStatus; User::RequestComplete( status, KErrNone );

// ----------------------------------------------------------------------------- // CMyActiveObject::DoCancel // Description: This calls CActive's Cancel // ----------------------------------------------------------------------------- // void CMyActiveObject::DoCancel()

// ----------------------------------------------------------------------------- // CMyActiveObject::SetIncrementCounter // Description: Sets Increment Counter of CMyActiveObject // ----------------------------------------------------------------------------- // void CMyActiveObject::SetIncrementCounter( TInt aCount) iIncrementCount = aCount;

// ----------------------------------------------------------------------------- // CMyActiveObject::RunL // Description: CActive::RunL implementation which will either stop the // Scheduler or increment the count // ----------------------------------------------------------------------------- // void CMyActiveObject::RunL() switch(iRequest) case ECount: iCount += iIncrementCount; break; case EStop: CActiveScheduler::Stop(); break; default: break;

// ----------------------------------------------------------------------------- // CMyActiveObject::CountValue // Description: Returns Count Value of CMyActiveObject // ----------------------------------------------------------------------------- // TUint CMyActiveObject::CountValue () const return iCount;

======================================================= //Usage... SomeOtherCpp.cpp

#include "MyActiveObject.h"

const TInt KErrActiveObjCount = -1001;

TInt UseMyActiveObjectL( TUint aCount ) TInt retVal ( KErrNone );

//Create one Active Object for Count purpose CMyActiveObject* object1 = new( ELeave ) CMyActiveObject; object1->SetIncrementCounter( aCount); object1->RequestFunction( CMyActiveObject::ECount ); CleanupStack::PushL( object1 );

//Create one Active Object for Stopping Scheduler CMyActiveObject* object2 = new( ELeave ) CMyActiveObject; object2->RequestFunction( CMyActiveObject::EStop ); //Issue the Request object1->ReadyToGo(); object2->ReadyToGo();

//Start the Scheduler CActiveScheduler::Start(); if( object1->CountValue() != aCount ) retVal = KErrActiveObjCount; delete object2; CleanupStack::PopAndDestroy( object1 ); return retVal;


I hope you might have got some idea now...

Regards Girish

> Symbian Active Object Framework - A Classic Example for Open C

Hi Girish,

Thanks for the help i will try with ur code.If incase Iam unable to get the desired output i will contact you.

Thanks for the help.

> Symbian Active Object Framework - A Classic Example for Open C

You have to use ... tags around your code to make it looks like code !!! I have copy pasted your code at the end of the article.

> Symbian Active Object Framework - A Classic Example for Open C

Hi, Read through your tutorial and have this strange problem with using multiple Active Objects. Two to be precise. I have two active objects, 1 to monitor the recent call log and the other to monitor the Line status of the phone. I am posting the .cpp code for both below.

For the Line Manager

CStatusManager* CStatusManager::NewL(MStatusManagerObserver* aObserver, TInt aTimeout)
{
 CStatusManager* self = new (ELeave) CStatusManager(aObserver, aTimeout);
 CleanupStack::PushL(self);
 self->ConstructL();
 CleanupStack::Pop(self);
 return self;
}

CStatusManager::CStatusManager(MStatusManagerObserver* aObserver, TInt aTimeout)
 : CActive(CActive::EPriorityHigh), iObserver(aObserver), iTimeout(aTimeout)
{
 CActiveScheduler::Add(this);
}

CStatusManager::~CStatusManager()
{
 Cancel();
 iTimer.Close();
 delete iObserver;

 //Added this
 iLine.Close();
 iPhone.Close();
 iTelServer.Close();
 //End Add
}

void CStatusManager::ConstructL()
{
        TInt ret;
        ret=iTelServer.Connect();
        TInt Numberphones;
        ret=iTelServer.EnumeratePhones(Numberphones);
        if(Numberphones>0)
        {
                GetDefaultTSY();
                TInt ret=iTelServer.LoadPhoneModule(iTsyName);
                if(ret!=KErrNone)
                {
                        return;
                }
        }
        else
        {
                User::After(5000000);
                return;
        }
        //Creating a phone connected to server
        RTelServer::TPhoneInfo PhoneInfo;
        iTelServer.GetPhoneInfo(0,PhoneInfo);
        iPhone.Open(iTelServer,PhoneInfo.iName);
        TInt aCount;
        iPhone.EnumerateLines(aCount);
        if(aCount>0)
        {
                RPhone::TLineInfo LineInfo;
                iPhone.GetLineInfo(0,LineInfo);
                TInt ret=iLine.Open(iPhone,LineInfo.iName);
                //iObserver->DispInt(ret);
        }
}

void CStatusManager::Reset()
{
 Cancel();
 Start();
}

void CStatusManager::DoCancel()
{
  iLine.NotifyStatusChangeCancel();
}

void CStatusManager::Start()
{
 if (!IsActive())
 {
        Cancel();//Just in case
        iLine.NotifyStatusChange(iStatus,iLineStatus);
        SetActive();
 }
}

void CStatusManager::RunL()
{
 TBuf<99> Msg;
 char a[99];
 if (iStatus == KErrNone)
 {
          switch(iLineStatus)
          {
                 //Monitor different line status here
          }
          iLine.NotifyStatusChange(iStatus,iLineStatus);
          SetActive();
 }
 else
 {
               //this is debuggin code
                iObserver->StatusChange(this,"Crap");
                TInt a=iStatus.Int();
               iObserver->DispInt(a);
                User::After(5000000);
                this->Deque();
                ConstructL();
                Start();
 }
}


void CStatusManager::GetDefaultTSY()
{
        TUint32 modemId = 0;
        CCommsDatabase* db = CCommsDatabase::NewL(EDatabaseTypeUnspecified);
        CleanupStack::PushL(db);
        db->GetGlobalSettingL(TPtrC(MODEM_DATA_FAX), modemId);
        // Now read the TSY name from the modem table record
        CCommsDbTableView* tableView =
           db->OpenViewMatchingUintLC (TPtrC(MODEM),TPtrC(COMMDB_ID),modemId);
        User::LeaveIfError(tableView->GotoFirstRecord());
        tableView->ReadTextL(TPtrC(MODEM_TSY_NAME),iTsyName);
        CleanupStack::PopAndDestroy(tableView);
        CleanupStack::PopAndDestroy(db);
}

void MStatusManagerObserver::AccessCallLog()
{
}

void MStatusManagerObserver::StatusChange(CStatusManager* aStatusManager,char *status)
{
}

void MStatusManagerObserver::StatusDetected(CStatusManager* aStatusManager)
{
}

void MStatusManagerObserver::InStatusDetected(CStatusManager* aStatusManager)
{
}

void MStatusManagerObserver::DispInt(TInt &aArg)
{
}

For Accessing the Call Logs

CInCallNumber* CInCallNumber::NewL(MNumberObserver* aObserver)
{
 CInCallNumber* self = new (ELeave) CInCallNumber(aObserver);
 CleanupStack::PushL(self);
 self->ConstructL();
 CleanupStack::Pop(self);
 return self;
}

CInCallNumber::CInCallNumber(MNumberObserver* aObserver)
 : CActive(CActive::EPriorityHigh), iObserver(aObserver)
{
 CActiveScheduler::Add(this);
}

CInCallNumber::~CInCallNumber()
{

 Cancel();
 delete iObserver;
}

void CInCallNumber::ConstructL()
{
        RFs aFs;
        aFs.Connect();
        iLogClient = CLogClient::NewL(aFs);
        User::LeaveIfNull(iLogClient);
       iLogViewRecent = CLogViewRecent::NewL(*iLogClient);
        User::LeaveIfNull(iLogViewRecent);
       iLogFilter = CLogFilter::NewL();
        User::LeaveIfNull(iLogFilter);
        iLogFilter->SetEventType(KLogCallEventTypeUid);
        //SetupFilterL(iLogViewRecent,iLogFilter);
        _LIT(Error,"Ended ConstructL of CallLg");
        TBuf<50> temp;
        temp.Copy(Error);
        iObserver->DisplayNumber(temp);
}

void CInCallNumber::DoCancel()
{
 iLogClient->NotifyChangeCancel();
}

void CInCallNumber::SetupNotify()
{
 if (!IsActive())
 {
        iLogViewRecent->SetRecentListL(KLogNullRecentList,*iLogFilter,iStatus);
        SetActive();
 }
}

void CInCallNumber::RunL()
{
        if(iStatus==KErrNone)
        {
        //        User::After(1000000);
                iNumCall=iLogViewRecent->CountL();
                //iObserver->DisplayInt(iNumCall);
                if(iNumCall>0)
                {       
                        iObserver->StatusChange(this,"dialling");                         
                        const CLogEvent& event=iLogViewRecent->Event();
                        TBuf<20> icallno=event.Number();
                        iObserver->DisplayNumber(icallno);
                        WForNot=EFalse;
                }
                else
                {
                        iObserver->StatusChange(this,"idd");                         
                }
        }
        else
        {
                _LIT(Error,"got an error from get number");
                TBuf<30> temp;
                temp.Copy(Error);
                iObserver->DisplayNumber(temp);
        }
}


void MNumberObserver::StatusChange(CInCallNumber* aStatusManager,char *status)
{
}

void MNumberObserver::StatusDetected(CInCallNumber* aStatusManager)
{
}

void MNumberObserver::InStatusDetected(CInCallNumber* aStatusManager)
{
}

void MNumberObserver::DisplayNumber(TDesC &aArg)
{
}

and this is how I am calling them from a Main Function.

        CActiveScheduler *sched = new (ELeave) CActiveScheduler();
        CleanupStack::PushL(sched);
        CActiveScheduler::Install(sched);

        //For getting the Number       
        NumObserver=new (ELeave) MNumberObserver();
        CleanupStack::PushL(NumObserver);
        ExNumber=CInCallNumber::NewL(NumObserver);
        CleanupStack::Pop(NumObserver);
        ExNumber->SetupNotify();
       
        //Observing the line status
        CStatusManager* iStatusManager;
       MStatusManagerObserver *iStatusManagerObserver;
        iStatusManagerObserver = new (ELeave) MStatusManagerObserver();
        CleanupStack::PushL(iStatusManagerObserver);
       iStatusManager = CStatusManager::NewL(iStatusManagerObserver, 10);
        CleanupStack::Pop(iStatusManagerObserver);       
        iStatusManager->Start();
        CActiveScheduler::Start();

Ok so after this really long code, just wanted to add that I am developing a console application. Now When i run this application of an Nokia 6620 phone, the RunL of the Line status function executes, but the iStatus is KRequestPending. Both of these AOs work fine independently.. so I am not sure what to do in order to figure out what the problem is. The issue seems to be with the time when both the asynchronus requests are isseud. Any guidance would be apprieciated.. Thanks
 alok

> Symbian Active Object Framework - A Classic Example for Open C

I figured out the problem with my code.. and it was something stupid.. Basically in the header file for The LIne status monitoring I had declared a TRequestStatus iStatus variable... which was confusing that Active Object as to which iState variable would be used. Dumb... but it tok me a day and a half to figure it out.. hope that helps anybody else who runs into something similar..

Symbian Active Object Framework - A Classic Example for Open Clo

In the header file above, a tilde is missing from the destructor prototype.

Symbian Active Object Framework - A Classic Example for Open Clo

I am very new in Symbian but i have question here on the code example. In the function

CMyActiveObject::RequestFunction( TRequestType aRequest ) iRequest = aRequest; iStatus = KRequestPending;

We should not be setting the istatus variable with the value as set above. Is it right(correct me if not)?

Could someone post an working example code for active objects please(.h .cpp, and client code which uses these)

Thanks in advance... Sreenn

Symbian Active Object Framework - A Classic Example for Open Clo

This is a very bad example of the use of active objects if the intention is to try and help beginners know how to use them.

The reason is its stopping the active scheduler. An active object shouldn't start or stop the active scheduler (and if it does its a very advanced case and you must definatly know exactly what you are doing any why before doing that).

A test active object can start and stop the active scheduler, but a production active object shouldn't. So for any beginners reading this article, please don't think this is a good example.

Symbian Active Object Framework - A Classic Example

Hi,

I also a beginner in this active objects and i have watched ur replies. Can u send me a simple
example for understanding active objects.

Thanks and regards,

Jobin

Re: Symbian Active Object Framework - A Classic Example for Open

Hi,

I also a beginner in this active objects and i have watched ur replies. Can u send me a simple
example for understanding active objects.

Thanks and regards,
Satish Khatri