Next: , Previous: , Up: Top   [Contents][Index]


3 The C++ API

The C++ API provides users an easy to use, object-oriented interface to a wide range of file monitoring APIs. This API provides a common facade to a set of heterogeneous APIs that not only greatly simplifies their usage, but provides an indirection layer that makes applications more portable: as far as there is an available monitor in another platform, an existing application will just work.

In reality, a monitor may have platform-specific behaviours that should be taken into account when writing portable applications using this library. This differences complicate the task of writing portable applications that are truly independent of the file monitoring API they may be using. However, monitors try to ‘compensate’ for any behavioural difference across implementations.

The typical usage pattern of this API is similar to the following:

3.1 Monitor Discovery

Since multiple monitor implementations exist and the caller potentially ignores which monitors will be available at run time, there must exist a way to query the API for the list of available monitor and request a particular instance. The monitor_factory is an object factory class that provides basic monitor registration and discovery functionality: API clients can query the monitor registry to get a list of available monitors and get an instance of a monitor either by type or by name.

The monitor_factory class provides the following methods:

static monitor * create_monitor

Creates a monitor of the specified type with the specified constructor parameters (see Monitors). A monitor of the platform default type can be created if fsw_monitor_type::system_default_monitor_type. If the named monitor is not available, then return nullptr.

static monitor * create_monitor_by_name

Creates a monitor of the specified type (by name) and constructor parameters (see Monitors). If the named monitor is not available, then return nullptr.

static std::vector<std::string> get_types()

Get the list of available monitor types. The type name can then be used to get a monitor instance by name using create_monitor_by_name.

static bool exists_type(const std::string& name)

Query whether the specified monitor exists.

static void register_type(const std::string& name, fsw_monitor_type type)

Register a monitor type in the list of available implementations.

3.2 Monitor Registration

In order for monitor types to be visible to the factory they have to be registered. Currently they can be registered using two helper macros, defined in monitor.h:

REGISTER_MONITOR(classname, monitor_type)

This macro must be invoked into a class’ header file and must be passed the class name and the monitor type.

REGISTER_MONITOR_IMPL(classname, monitor_type)

This macro must be invoked into a class’ source file and must be passed the class name and the monitor type.

The same monitor type cannot be used to register multiple monitor implementations. No checks are in place to detect this situation and the registration will succeed; however, the registration process of multiple monitor implementations for the same monitor type is not deterministic.

3.3 Monitors

The monitor class is the fundamental type of the C++ API: it defines the interface of every monitor and provides common functionality to inheritors of this class.

The public interface of a monitor is the following:

class monitor
{
public:
  monitor(std::vector<std::string> paths,
          FSW_EVENT_CALLBACK * callback,
          void * context = nullptr);
  virtual ~monitor();

  monitor(const monitor& orig) = delete;
  monitor& operator=(const monitor & that) = delete;

  void set_properties(
    std::map<std::string, std::string> options);
  std::string get_property(std::string name);
  void set_latency(double latency);
  void set_allow_overflow(bool overflow);
  void set_recursive(bool recursive);
  void set_directory_only(bool directory_only);
  void add_filter(const monitor_filter &filter);
  void set_filters(
    const std::vector<monitor_filter> &filters);
  void set_follow_symlinks(bool follow);
  void * get_context() const;
  void set_context(void * context);
  void start();
  void add_event_type_filter(
    const fsw_event_type_filter &filter);
  void set_event_type_filters(
    const std::vector<fsw_event_type_filter> &filters);
}

A monitor is thus a type with the following characteristics:

3.3.1 Business Interface

The business interface of the monitor class is the following:

void set_properties(std::map<std::string, std::string> options);

This function sets a map of monitor-specific properties.

std::string get_property(std::string name);

This function returns the property named name.

void set_allow_overflow(bool allow_overflow);

This function sets the allow overflow flag to the specified value. If this flag is set, then the monitor will report a monitor buffer overflow as a change event of type fsw_event_flag::Overflow.

void set_latency(double latency);

This function sets the latency of the monitor in seconds. This method only sets the latency value. The exact meaning of latency and how it is enforced depends on a monitor implementation.

void set_recursive(bool recursive);

This function sets the recursive flag of the monitor to indicate whether the monitor should recursively observe the contents of directories.

void set_directory_only(bool directory_only);

This function sets the directory only flag to the specified value. If this flag is set, then the monitor will only watch directories during a recursive scan. This functionality is only supported by monitors whose backend fires change events on a directory when one its children is changed. If a monitor backend does not support this functionality, the flag is ignored.

void add_filter(const monitor_filter &filter);

This function adds a monitor_filter instance to the filter list of the current monitor.

void set_filters(const std::vector<monitor_filter> &filters);

This function sets the filter list of the current monitor, substituting existing filter if any.

void set_follow_symlinks(bool follow);

This function sets the follow_symlinks flag of the monitor to indicate whether the monitor should follow observed symbolic links or observe the links themselves.

void * get_context() const;

This function gest the pointer to the context data that is passed to the callback by the monitor.

void set_context(void * context);

This function sets the pointer to the context data that is passed to the callback by the monitor.

void start();

This function starts the monitor so that it begins listening to file system change events.

void add_event_type_filter(const fsw_event_type_filter &filter);

This function adds a fsw_event_type_filter instance to the event type filter list of the current monitor.

void set_event_type_filters(const std::vector<fsw_event_type_filter> &filters);

This function sets the event type filter list of the current monitor, substituting existing filters if any.

3.3.2 Implementing Monitors

monitor is a class that declares the following protected functions:

bool accept_event_type(fsw_event_flag event_type) const

This function checks whether the specified event_type can be accepted according to the list of event type filters of the monitor.

bool accept_path(const std::string &path) const

This function checks whether the specified path can be accepted according to the list of filters of the monitor.

bool accept_path(const char *path) const

This function checks whether the specified path can be accepted according to the list of filters of the monitor.

void notify_events(const std::vector<event> &events) const

This function notifies that detection of the specified events.

void notify_overflow(const std::string & path) const

This function notifies that the monitor has overflowed. Overflowing is a monitor-specific concept and not all monitors experience this behaviour.

std::vector<fsw_event_flag> filter_flags(const event &evt) const

This function filters the list of flags of an event evt using the list of event type filters of the monitor. If no filters are set, all the flags are returned.

virtual void run() = 0

This pure virtual function shall contain the logic of a monitor implementation. This function will be invoked by the monitor’s start API function.

Since it contains a pure virtual function, run(), the monitor class is abstract. Inheritors are required to provide an implementation of the run() function containing the monitor logic and its ‘event loop’.

3.3.3 The Anatomy of a Typical Monitor

The anatomy of monitors is typically very similar and it can be illustrated with the following algorithm (written in pseudo-code):

void run()
{
  initialize_api();

  while (true)
  {
    scan_paths();
    wait_for_events(latency);

    vector<change_events> evts = get_changes();
    vector<event> events;

    for (auto & evt : evts)
    {
      if (accept(evt.get_path))
      {
        events.push_back({event from evt});
      }
    }

    if (events.size())
    {
      notify_events(events);
    }
  }
}

Despite being a minimal implementation, this algorithm exemplifies the common tasks performed by a monitor:

3.4 Events

Events are modeled by the fsw::event class, defined in the event.h header:

class event
{
public:
  event(std::string path,
        time_t evt_time,
        std::vector<fsw_event_flag> flags);
  virtual ~event();

  std::string get_path() const;
  time_t get_time() const;
  std::vector<fsw_event_flag> get_flags() const;

  static fsw_event_flag
    get_event_flag_by_name(const std::string &name);
  static std::string
    get_event_flag_name(const fsw_event_flag &flag);

private:
  std::string path;
  time_t evt_time;
  std::vector<fsw_event_flag> evt_flags;
};

The event class provides a simple and uniform representation of an event to all the API. An event has got the following characteristics:

Currently the API provides no way for monitor implementors to provide additional, monitor-dependent fields to an event. Since the API stores events by value into collections (such as vector), an extended event would be sliced and additional fields would be lost.

3.4.1 Looking Up Event Types by Name

Event types can be looked up by name using the following function:

fsw_event_flag get_event_flag_by_name(const std::string &name);

Returns the fsw_event_flag instance whose name is name. If no instance is found with the specified name, this function will throw a libfsw_exception.

3.4.2 Getting the Name of an Event Type

The name of an event type can be obtained using the following function:

static std::string get_event_flag_name(const fsw_event_flag &flag);

This function returns the name of the specified event type.

Most of the times, the name of an event type is used when writing user output: to ease this task, the event.h header defines the following operator overload:

ostream& operator<<(ostream& out, const fsw_event_flag flag);

This operator writes the name of the specified fsw_event_flag to the stream.

3.5 Path Filters

Path filters are regular expression used to accept or reject file change events based on the value of their path. A filter is represented by the fsw::monitor_filter type, defined in the filter.h header:

typedef struct monitor_filter
{
  std::string text;
  fsw_filter_type type;
  bool case_sensitive;
  bool extended;
} monitor_filter;

and has the following characteristics:

text

The regular expression used to match paths.

type

The filter type can either be an inclusion or exclusion filter.

case_sensitive

A flag indicating the filter case sensitivitiy.

extended

A flag indicating whether text is an extended regular expression.

3.5.1 Filter Types

A filter type determine whether the filter regular expression is used to include and exclude paths from the list of the events processed by the library. libfswatch processes filters this way:

Said another way:

The fswatch Info documentation has a user-oriented discussion of how filters are used.

3.6 Event Type Filters

Event type filters let callers filter the events using a specified set of event types. An event type filter is represented by the fsw_event_type_filter type, defined in the cfilter.h header:

typedef struct fsw_event_type_filter
{
  fsw_event_flag flag;
} fsw_event_type_filter;

Next: , Previous: , Up: Top   [Contents][Index]