# app_updates.h

Many App Services need to frequently send out messages to the Apps to do life updates of the UI. Care must be taken, that these updates don't monopolize the bandwidth nor the CPU time, so that the Apps become slow on reaction to user actions. The library app_updates implements a strategy where updates are sent out fast, but not in a single loop over all sessions/updates but in smaller pieces. After each piece the next piece is scheduled with SetIoExec, so there is opportunity to process incoming requests. Sending response to these incoming requests is given priority to sending the updates.

## File information

File | common/lib/app_updates.h
Basic ideas
Classes | AppUpdates AppUpdatesSession AppUpdate AppUpdatesQueued AppUpdatesUser AppUpdatesFilterY AppUpdatesFilter AppUpdatesFilters
Tutorial

## Basic ideas

- A class AppUpates is used to keep all information needed per instance. These are a lists of all sessions, a queue of updates and the current update filter being processed. The main instance class, which uses AppInstance as base class should use this class as base class as well.
- For each type of update, a class should be defined, using AppUpdate as a base class. For each such class a corresponding filter should be defined as class using AppUpdatesFilter<update-class> as base class. The AppUpdates::StartUpdate function is used to put this class into the updates queue and starts the update process.
- For each filter a seperate list per instance is used. The type of this list is AppUpdatesFilters<update-class>
- For the update process all filters in the list for this type of update are processed. All filters in this list are checked one after the other if the Update needs to be sent, but only a limited number at a time to avoid CPU monopolization.
- When a filter is scheduled for processing the update is sent right away, if there is no sending of an update pending for the session already. If sending of an update is pending, a AppUpdatesQueued object is allocated and put in the AppUpdatesSession::queue list. Whenever sending of an update on a givend session is complete, the entries in these lists are processed and the AppUpatesQueued object is deleted. The AppUpdate object is deleted when all sessions are processed and no AppUpdatesQueued object is allocated for this AppUpdate anymore.
- Some types of updates need to be sent to the sessions of a given user only. Since this is a frequent case an optimization is implemented, so that not all filters need to be checked in this case. For each filter list there is a userTree of all users currently having filters. When a new filter is created it is checked if the user is already in this tree. If not an object AppUpdatesUser is allocated and put in this tree. AppUpdatesUser::filters is a pointer to the first filter of this user in the filters list. New filters are put in the list before any filter existing for the user already.
  The filters list can be proccesed started from AppUpdatesObject::filters until the user changes to cover all filters of a given user.
- The AppUpdatesFilters<update-class> objects are put in a tree per session, using src and a reference to the filter list as key. When a new filter is created and for the same type of filter a filter with the same src already exists for the given session the existing filter is deleted and replaced by the new filter.

# Classes

## AppUpdates

    class AppUpdates : public UIoExec {
        friend class AppUpdatesSession;

        void IoExec(void * execContext);
        IIoMux * iomux;
        istd::list<class AppUpdatesSession> sessions;
        istd::list<class AppUpdate> updates;
        class AppUpdatesFilterY * currentFilter;
        ulong64 nextSessionId;

    public:
        AppUpdates(IIoMux * const iomux);
        void StartUpdate(class AppUpdate * update);
    };

This class is used as base class for the instance class. It is used for all the instance global lists for updates.

### AppUpdates

The constructor needs iomux for the SetExec mechanism

### StartUpdate

To be called to start the update process.

## AppUpdatesSession

    class AppUpdatesSession : public AppWebsocket, public istd::listElement<AppUpdatesSession> {
        friend class AppUpdates;
        friend class AppUpdatesFilterY;

        void AppWebsocketSendResult();
        void virtual ResponseSent() = 0;
        void virtual UpdateSent();

        bool sendingUpdate;
        bool sendingResponse;
        class btree * srcs;
        istd::list queue;

    public:
        AppUpdatesSession(class AppUpdates * app, IWebserverPlugin * plugin, class IInstanceLog * const log, class JsonApiContext * jsonApiContext);
        ~AppUpdatesSession();
        void SendResponse(class json_io & msg, char * buffer);
        void SendUpdate(class json_io & msg, char * buffer);
        void AppFilterClose(istd::list<AppUpdatesFilterY> & filters, const char * src);
        class AppUpdatesFilterY * AppFilterGet(istd::list<AppUpdatesFilterY> & filters, const char * src);

        class AppUpdates * instance;
        ulong64 sessionId;
        unsigned responsePending;
    };

### AppUpdatesSession

Constructor

### SendResponse

This function should be called to send responses on the Websocket connection.

### SendUpdate

This function should be called to send updates on the Websocket connection.

### AppFilterClose

Function which may be called to explictly close a filter on a given session. The filter is identified by src and the filters reference from the AppUpdatesFilters class is used to identify the type of filter.

### sessionId

unique session id. Maybe used in filters for example to avoid sending updates to sessions which originated the action causing the update.

## AppUpdate

    class AppUpdate : public istd::listElement<AppUpdate> {
        friend class AppUpdates;
        friend class AppUpdatesQueued;

        unsigned queuedCount;
        class AppUpdatesUser * user;
        istd::list<AppUpdatesFilterY> * filters;

    public:
        AppUpdate(istd::list<AppUpdatesFilterY> & filters, class btree * & usersTree, const char * sip = 0);
        virtual ~AppUpdate();
    };

### AppUpdate

Constructor. filters and usersTree should be used from the corresponding AppFilters class. sip is used to identify the user, if the update is to be sent to session of this user only.

## AppUpdatesQueued

    class AppUpdatesQueued : public AppUpdatesQueuedSession, public AppUpdatesQueuedFilter {
        friend class AppUpdates;
        friend class AppUpdatesSession;

        AppUpdatesQueued(class AppUpdate * update, class AppUpdatesFilterY * filter);
        ~AppUpdatesQueued();

        class AppUpdate * update;
        class AppUpdatesFilterY * filter;
    };

Internal class to queue updates on a session.

## AppUpdatesUser

    class AppUpdatesUser : public btree {
        friend class AppUpdates;
        friend class AppUpdate;
        friend class AppUpdatesSession;
        friend class AppUpdatesFilterY;

        AppUpdatesUser(class AppUpdatesFilterY * filter, class btree * & tree, const char * sip);
        ~AppUpdatesUser();

        int btree_compare(void * key) { return strcmp((const char *)key, sip); };
        int btree_compare(class btree * b) { return strcmp(((class AppUpdatesUser *)b)->sip, sip); };

        const char * sip;
        class AppUpdatesFilterY * filters;
        class btree * & tree;
        unsigned refCount;
    };

Internal class to determine the first session of a given user.

## AppUpdatesFilterY

    class AppUpdatesFilterY : public istd::listElement<AppUpdatesFilterY>, public btree {
        friend class AppUpdates;
        friend class AppUpdatesSession;

        int btree_compare(void * key) {
            struct filters_ctx * ctx = (struct filters_ctx *)key;
            if (ctx->filters == filters) return strcmp(ctx->src, src);
            else if (ctx->filters > filters) return 1;
            return -1;
        }
        int btree_compare(class btree * b) {
            if (((class AppUpdatesFilterY *)b)->filters == filters) return strcmp(((class AppUpdatesFilterY *)b)->src, src);
            else if (((class AppUpdatesFilterY *)b)->filters > filters) return 1;
            return -1;
        }
        virtual bool Test(class AppUpdate * update) = 0;
        virtual void Send(class AppUpdate * update) = 0;

        class AppUpdatesUser * user;
        istd::list<class AppUpdatesQueuedFilter> queue;
        char * src;
        void * filters;
        class AppUpdatesSession * session;

    public:
        AppUpdatesFilterY(istd::list<AppUpdatesFilterY> & filters, class btree * & usersTree, class AppUpdatesSession * session, const char * src, const char * sip);
        virtual ~AppUpdatesFilterY();
        ulong64 GetSessionId() { return session->sessionId; };
        void SendUpdate(class json_io & msg, char * buffer) { session->SendUpdate(msg, buffer); };
        const char * GetSrc() { return src; };
        const char * GetUser() { return user ? user->sip : 0; };
    };

Internal class used as base class for filters.

## AppUpdatesFilter

    template <class U> class AppUpdatesFilter : public AppUpdatesFilterY {
        bool Test(class AppUpdate * update) { return Test((U *)update); };
        void Send(class AppUpdate * update) { Send((U *)update); };
        virtual bool Test(U * update) = 0;
        virtual void Send(U * update) = 0;

    public:
        AppUpdatesFilter(AppUpdatesFilters<U> & filters, class AppUpdatesSession * session, const char * src, const char * sip = 0) : AppUpdatesFilterY(filters.list, filters.usersTree, session, src, sip) { };
        virtual ~AppUpdatesFilter() {};
    };

Internal class used as base class for filters.

### AppUpdatesFilter

Constructor. src is a string identifying the filter. If a filter with this name already exists for the session, the old filter is deleted. If src is null this is also a valid identifiers if another filter with src equals null already exists this is deleted.

### AppUpdatesFilter

Constructor. src is the unique identifier for the filter on a given session of a given type. Use sip to identify users, if there are updates sent to single users only.

### Test

Function to test if an update needs to be sent.

### Send

Function called when the update is to be sent.

## AppUpdatesFilters

    template <class U> class AppUpdatesFilters {
    public:
        AppUpdatesFilters() {
            usersTree = 0;
        }
        istd::list<AppUpdatesFilterY> list;
        class btree * usersTree;
    };

# Tutorial

The following steps have to be done to use the app_update library in an App.

1. Use AppUpdates as base class for your App instance class

   This class holds the list of updates queued and schedules updates in an IoExec handler.

    class myApp : public AppInstance, public JsonApiContext, ... , public AppUpdates {
        private:
                ...
        public:
                ...
             class AppUpdatesFilters         filters;
    };

2. Use AppUpdatesSession as base class for your websocket session object

   The AppUpdatesSession coordinates messages sent to this websocket session. For this it contains the AppWebsocket class as base class. Don't override AppWebsocketSendResult as it is used in AppUpdatesSession.

   The variables

   bool sendingUpdate
   bool sendingResponse
   Keep trace what kind of message is beeing sent.

       class MySession : public AppUpdatesSession {
           private:
               ...

               char * AppWebsocketPassword() override;
               bool   AppWebsocketConnectComplete(class json_io & msg, word info) override;
               void   AppWebsocketMessage(class json_io & msg, word base, const char * mt, const char * src) override;
               void   AppWebsocketClosed() override;
               //void AppWebsocketSendResult() override;
       }

       void MySession::AppWebsocketMessage(class json_io & msg, word base, const char * mt, const char * src) {
            ...
            new MyUpdatesFilter(this->appInstance->filters, this, src, sip);
            ...
       }

3. Use class AppUpdate as base class for objects holding update information

   For any update to be sent to multiple Apps, there must be an object, which holds the information about the update. These objects should use the class AppUpdate as base class.

        class MyUpdate : public AppUpdate {
           public:
                MyUpdate(const char * myMessage, class AppUpdatesFilters & filters, const char * sip = 0);
               ~MyUpdate();

                char        * myMessage;
        };

4. Use AppUpdatesFilter<update-class> as base class the filter objects

   For each type of update a corresponding filter class has to be defined

   The virtual functions

   virtual bool Test(U * update)
   virtual void Send(U * update)
   need to be overriden. Test should return true if the update needs to be sent. Then Send is called to send the update. Use AppUpdatesFilter::SendUpdate(class json_io & msg, char * buffer) for sending.

       class MyUpdatesFilter : public AppUpdatesFilter {
           bool Test(MyUpdate * update) override;
           void Send(MyUpdate * update) override;

           public:
           MyUpdatesFilter(AppUpdatesFilters & filters, class AppUpdatesSession * session, const char * src, const char * sip);
           ~MyUpdatesFilter();
       };

           ...
       void MyUpdatesFilter::Send(MyUpdate * update) {
           char sb[16000];
           class json_io send(sb);
           word base = send.add_object(0xffff, 0);
           send.add_string(base, "text", update->myMessage);
           if (GetSrc()) send.add_string(base, "src", GetSrc());
           SendUpdate(send, sb);
       }

5. Define filter list variables in the App Instance class

   For each update/filter type a list is needed per App Instance. Define public members
   AppUpdatesFilters<update-class> assignedFilters
   in the App Instance class for this. (see example 1)

6. Use AppUpdates::StartUpdate to start any update process

    appInstance->StartUpdate(new MyUpdate("my message to be sent to all sessions where Test(MyUpdate * update) returns true", this->appInstance->filters));