text: /***************************************************************************************************
   * Copyright 2019 NVIDIA Corporation. All rights reserved.
   **************************************************************************************************/
  /// \file
  /// \brief  Bridge server
  
  #ifndef MI_NEURAYLIB_IBRIDGE_SERVER_H
  #define MI_NEURAYLIB_IBRIDGE_SERVER_H
  
  #include <mi/base/enums.h>
  #include <mi/base/handle.h>
  #include <mi/base/interface_implement.h>
  #include <mi/base/uuid.h>
  #include <mi/neuraylib/iserializer.h>
  #include <mi/neuraylib/itransaction.h>
  #include <mi/neuraylib/iuser_class_factory.h>
  
  namespace mi {
  
  class IString;
  class IMap;
  
  namespace http { class IServer; }
  
  namespace neuraylib { class ICanvas; class IImport_result; class ITag_set; class IExport_result; }
  
  namespace bridge {
  
  class IApplication;
  class IApplication_session_handler;
  class IElement_set;
  class IServer_session;
  class IServer_transaction;
  class IServer_video_context;
  
  /** \defgroup mi_neuray_bridge_server Bridge server
      \ingroup mi_neuray_bridge
  
      The Bridge server is a single remote host that makes its resources available to Bridge
      clients through a proprietary network protocol over a single TCP/IP connection. The client uses
      the connection to send changes and delegate work to the Bridge server; the server uses the
      connection to send job results and other data back to the client.
  
      A Bridge server can serve multiple clients at the same time. This is equivalent to starting
      multiple jobs simultaneously on a host in a normal local cluster.
  
      \if IRAY_API The API consists of two layers: a low-level API, called \em Bridge \em API, and a
      high-level API, called \em Iray \em Bridge \em API. The low-level Bridge API is generic and
      independent of domain-specific tasks like rendering. It exposes the full flexibility of the
      Bridge. The high-level Iray Bridge API is specific to rendering. It encapsulates the details of
      the Bridge API and exposes them in a simpler way sufficient for rendering. Using the high-level
      API is strongly recommended unless there are specific needs which require using the low-level
      API. See the API component #mi::bridge::IBridge_server for the Bridge API, and
      #mi::bridge::IIray_bridge_server for the Iray Bridge API. \else See the API component
      #mi::bridge::IBridge_server for a starting point. \endif
  
      \see \ref mi_neuray_bridge, \ref mi_neuray_bridge_client
  */
  
  /** \addtogroup mi_neuray_bridge_server
  @{
  */
  
  class IServer_job_info;
  
  /// Represents the server-side part of a job that can be executed by the Bridge server.
  ///
  /// The corresponding client-side part of the job must return the same ID from
  /// #mi::neuraylib::ISerializable::get_class_id() and be registered as a Bridge job on the
  /// client.
  ///
  /// The server-side part must implement #mi::neuraylib::ISerializable::deserialize() to deserialize
  /// the data that is sent by #mi::bridge::IClient_job::serialize(), and
  /// #mi::bridge::IServer_job::execute() to serialize the result of the job which is consumed by
  /// #mi::bridge::IClient_job::receive_remote_result(),
  ///
  /// It is recommended to derived your implementation from #mi::bridge::Server_job and overriding
  /// only the required methods.
  class IServer_job : public
      mi::base::Interface_declare<0x555dea0f,0x4eeb,0x44b9,0xba,0x81,0x5a,0x42,0x3d,0xe5,0xf8,0x30,
      neuraylib::ISerializable>
  {
  public:
      /// Executes a job on behalf of a Bridge client.
      ///
      /// Intermediate results can be sent to the client by calling
      /// #mi::neuraylib::ISerializer::flush() on \p serializer. All data serialized until that point
      /// will be delivered in a single call to #mi::bridge::IClient_job::receive_remote_result() on
      /// the client with the \c last_result flag set to \c false. When #execute() returns any data
      /// not yet flushed will be flushed and #mi::bridge::IClient_job::receive_remote_result() will
      /// be called one last time with \c last_result flag set to \c true.
      ///
      /// Example 1: No intermediate results are required. The \c execute() method simply serializes
      ///            the result and returns. The \c receive_remote_result() method will be called once
      ///            with the full result and \c last_result flag set to \c true.
      ///
      /// Example 2: Progress feedback. During execution of the job periodically one #mi::Float32
      ///            representing the percentage of the work done is serialized periodically and the
      ///            serializer is flushed. Finally, when the job is done the final result is
      ///            serialized and the method returns. On the client side \c receive_remote_result()
      ///            parses a single #mi::Float32 for every call with the \c last_result flag set to
      ///            \c false and updates a progress bar. When \c last_result is \c true it
      ///            deserializes the entire result and is done.
      ///
      /// Example 3: Custom protocol. Whenever the server is done with a partial result it is flushed
      ///            as a chunk to the client for processing. The server is preparing multiple
      ///            different types of results, so the chunk always begins with an #mi::Uint8 which
      ///            identifies the type of the chunk and the data to expect. After flushing the last
      ///            chunk the \c execute() method returns without any pending data. On the client
      ///            side \c receive_remote_result() will check the \c last_result flag. If \c true,
      ///            then the job is done, no more data to parse. If \c false the first #mi::Uint8 is
      ///            deserialized and the rest of the data is deserialized depending on the type of
      ///            the chunk and then processed.
      ///
      /// \param transaction   The Bridge transaction to be used for the job execution.
      /// \param serializer    The serializer to be used to send the result back to the client.
      /// \param job_info      Additional information about this job.
      virtual void execute(IServer_transaction* transaction, neuraylib::ISerializer* serializer,
          IServer_job_info* job_info) = 0;
  
      /// Called if the Bridge transaction is aborted or if an error occurs.
      ///
      /// The job execution should be stopped as quickly as possible, no result will be written to the
      /// client.
      virtual void cancel() = 0;
  };
  
  /// This mixin class provides a default implementation for some of the methods needed by
  /// #mi::bridge::IServer_job.
  ///
  /// It is recommended to derive from this class rather than from #mi::bridge::IServer_job directly.
  template <Uint32 i_id1, Uint16 i_id2, Uint16 i_id3
  , Uint8 i_id4, Uint8 i_id5, Uint8 i_id6, Uint8 i_id7
  , Uint8 i_id8, Uint8 i_id9, Uint8 i_id10, Uint8 i_id11
  , class I = IServer_job>
  class Server_job : public base::Interface_implement<I>
  {
  public:
      /// Own type.
      typedef Server_job<i_id1,i_id2,i_id3,i_id4,i_id5,i_id6,i_id7,i_id8,i_id9,i_id10,i_id11,I> Self;
  
      /// Declares the interface ID
      typedef base::Uuid_t<i_id1,i_id2,i_id3,i_id4,i_id5,i_id6,i_id7,i_id8,i_id9,i_id10,i_id11> IID;
  
      /// Compares the interface ID \p iid against the interface ID of this interface and its
      /// ancestors.
      ///
      /// \return \c true if \p iid == \c IID() or is equal to one of the interface IDs of
      /// its ancestors, and \c false otherwise.
      static bool compare_iid( const base::Uuid& iid)
      {
          if( iid == IID())
              return true;
          return I::compare_iid( iid);
      }
  
      /// Acquires a const interface.
      ///
      /// If this interface is derived from or is the interface with the passed
      /// \p interface_id, then return a non-\c NULL \c const #mi::base::IInterface* that
      /// can be casted via \c static_cast to an interface pointer of the interface type
      /// corresponding to the passed \p interface_id. Otherwise return \c NULL.
      ///
      /// In the case of a non-\c NULL return value, the caller receives ownership of the new
      /// interface pointer, whose reference count has been retained once. The caller must
      /// release the returned interface pointer at the end to prevent a memory leak.
      virtual const base::IInterface* get_interface( const base::Uuid& interface_id) const
      {
          if( interface_id == IID()) {
              const Self* self = static_cast<const Self*>( this);
              self->retain();
              return self;
          }
          return I::get_interface_static( this, interface_id);
      }
  
      /// Acquires a mutable interface.
      ///
      /// If this interface is derived from or is the interface with the passed
      /// \p interface_id, then return a non-\c NULL #mi::base::IInterface* that
      /// can be casted via \c static_cast to an interface pointer of the interface type
      /// corresponding to the passed \p interface_id. Otherwise return \c NULL.
      ///
      /// In the case of a non-\c NULL return value, the caller receives ownership of the new
      /// interface pointer, whose reference count has been retained once. The caller must
      /// release the returned interface pointer at the end to prevent a memory leak.
      virtual base::IInterface* get_interface( const base::Uuid& interface_id)
      {
          if( interface_id == IID()) {
              Self* self = static_cast<Self*>( this);
              self->retain();
              return self;
          }
          return I::get_interface_static( this, interface_id);
      }
  
      using base::Interface_implement<I>::get_interface;
  
      /// Returns the class ID corresponding to the template parameters of this mixin class.
      virtual base::Uuid get_class_id() const
      {
          return IID();
      }
  
      /// Empty body. This method from the base class is not needed for client jobs.
      virtual void serialize( neuraylib::ISerializer* serializer) const
      {
          // avoid warnings
          (void) serializer;
      }
  
      /// Does nothing in the default implementation.
      virtual void cancel() { }
  };
  
  /// Provides additional information about a bridge job.
  class IServer_job_info : public
      mi::base::Interface_declare<0xa28b5525,0x728b,0x447a,0x89,0x9f,0x15,0x64,0xdf,0x14,0xc7,0xdc>
  {
  public:
      /// Returns the Bridge job instance. If the job has not been deserialized yet \c NULL is 
      /// returned. This is the case if the job data contains tags in which case it can't be 
      /// deserialized until the server-side database transaction has been started.
      virtual IServer_job* get_job() const = 0;
  
      /// Returns the Bridge job instance.
      ///
      /// This templated member function is a wrapper of the non-template variant for the user's
      /// convenience. It eliminates the need to call
      /// #mi::base::IInterface::get_interface(const Uuid&)
      /// on the returned pointer, since the return type already is a pointer to the type \p T
      /// specified as template parameter.
      template<class T>
      T* get_job() const
      {
          IServer_job* ptr_job = get_job();
          if ( !ptr_job)
              return 0;
          T* ptr_T = static_cast<T*>( ptr_job->get_interface( typename T::IID()));
          ptr_job->release();
          return ptr_T;
      }
  
      /// Returns the id of this job. The id is only guaranteed to be unique among the 
      /// currently active jobs of the transaction and is suitable only for log messages
      /// and the like.
      virtual const char* get_id() const = 0;
  
      /// Returns the universally unique identifier (UUID or GUID) of the Bridge job. This 
      /// will always be available even if the job has not been deserialized yet. The UUID
      /// defines the job class and client side and server side job implementations share 
      /// the same class id.
      virtual base::Uuid get_job_uuid() = 0;
  };
  
  /// A list of IServer_job instances.
  class IServer_job_list : public
      mi::base::Interface_declare<0xde301678,0x11e9,0x4bed,0x9b,0xc3,0x60,0x1e,0xcf,0xa3,0x88,0xa9>
  {
  public:
  
      /// Returns the number of jobs in the list.
      virtual Size get_size() = 0;
  
      /// Returns the job at the given index, or 0 if the index is out of bounds. The job must 
      /// be released when not needed anymore.
      virtual IServer_job_info* get_job(Size index) = 0;
  };
  
  /// Abstract interface for callbacks for transaction commit or abort events.
  ///
  /// \see #mi::bridge::IServer_transaction::add_transaction_callback(),
  ///      #mi::bridge::IServer_transaction::remove_transaction_callback()
  class IServer_transaction_callback : public
      mi::base::Interface_declare<0x7796c406,0xff8f,0x423d,0x8f,0x53,0x1a,0x66,0x50,0xcf,0x83,0x86>
  {
  public:
      /// This method is called when the Bridge transaction has been successfully committed.
      ///
      /// At this point any changes mirrored via the Bridge transaction have been committed to the
      /// server-side database.
      virtual void transaction_committed_callback() = 0;
  
      /// This method is called when the Bridge transaction has been aborted.
      ///
      /// Any changes mirrored via the Bridge transaction have \em not been committed to the
      /// server-side database.
      ///
      /// \param error_code
      ///                     -     0: The transaction was aborted by request of the client.
      ///                     -    -1: The transaction was canceled because of a connection error.
      ///                     - <= -2: The transaction was canceled because of an unspecified error.
      /// \param message      A short explanation of the error.
      virtual void transaction_aborted_callback( Sint32 error_code, const char* message) = 0;
  };
  
  /// Context for incremental snapshots.
  ///
  /// \see #mi::bridge::IServer_transaction::create_incremental_snapshot_context()
  class IIncremental_snapshot_context : public
      mi::base::Interface_declare<0x5ec24e12,0x9ce9,0x4f39,0x9f,0xd5,0x35,0xf1,0xe5,0x41,0x34,0x2e>
  {
  public:
      /// Creates an incremental snapshot.
      ///
      /// The incremental snapshot contains only the changes since the last call to this method on
      /// this context (or to the creation of this context for the first call).
      ///
      /// \param transaction        The transaction to be used.
      /// \param[out] snapshot_id   The ID of the snapshot will be stored here if the operation is
      ///                           successful.
      /// \return
      ///                           -     0: Success.
      ///                           - <= -1: Unspecified error.
      virtual Sint32 create_snapshot( IServer_transaction* transaction, IString* snapshot_id) = 0;
  };
  
  /// Database transactions started on the client will be automatically mirrored to the server 
  /// and exposed as instances of this class to executing Bridge jobs. The relative start and 
  /// commit order of the mirrored server side transactions will always be the same as on the 
  /// client. Note however that the server side transactions are started and committed 
  /// asynchronously so even after committing the client side transaction the mirrored server 
  /// side transaction might not even have been started yet. For the client to know when a 
  /// server side transaction has finished it must wait for the last Bridge job executed in the 
  /// transaction to complete.
  class IServer_transaction : public
      mi::base::Interface_declare<0x67fd848e,0xce43,0x4675,0x8b,0x14,0xb2,0x54,0xd,0xd2,0x29,0x63>
  {
  public:
      /// Returns the session of the transaction.
      virtual IServer_session* get_session() const = 0;
  
      /// Returns the local transaction corresponding to this Bridge transaction, or \c NULL if this
      /// transaction has not yet been started on the client. The IServer_transaction instance 
      /// passed to #mi::bridge::IServer_job::execute() will always be started. IServer_transaction 
      /// instances returned by #mi::bridge::IServer_session::get_pending_transactions() are 
      /// however not always started yet, in which case this method will return \c NULL.
      ///
      /// \return the database transaction or \c NULL if this bridge transaction has not yet 
      ///         been started.
      virtual neuraylib::ITransaction* get_database_transaction() const = 0;
  
      /// Returns the local transaction corresponding to this Bridge transaction.
      ///
      /// This templated member function is a wrapper of the non-template variant for the user's
      /// convenience. It eliminates the need to call
      /// #mi::base::IInterface::get_interface(const Uuid&)
      /// on the returned pointer, since the return type already is a pointer to the type \p T
      /// specified as template parameter.
      template<class T>
      T* get_database_transaction() const
      {
          neuraylib::ITransaction* ptr_itransaction = get_database_transaction();
          if ( !ptr_itransaction)
              return 0;
          T* ptr_T = static_cast<T*>( ptr_itransaction->get_interface( typename T::IID()));
          ptr_itransaction->release();
          return ptr_T;
      }
  
      /// Adds a transaction callback.
      ///
      /// The callback will be called when the transaction has been committed or aborted by the
      /// client, or if there is an error causing the transaction to be aborted.
      ///
      /// \see #remove_transaction_callback()
      ///
      /// \param callback   The callback to be added.
      /// \return
      ///                   -     0: Success.
      ///                   -    -1: Invalid argument (\p callback is \c NULL).
      ///                   -    -2: The transaction is not open.
      ///                   -    -3: The transaction callback was already added.
      ///                   - <= -4: Unspecified error.
      virtual Sint32 add_transaction_callback( IServer_transaction_callback* callback) = 0;
  
      /// Removes a transaction callback.
      ///
      /// \see #add_transaction_callback()
      ///
      /// \param callback   The callback to be removed.
      /// \return
      ///                   -     0: Success.
      ///                   -    -1: Invalid argument (\p callback is \c NULL).
      ///                   -    -2: There is no such callback.
      ///                   - <= -3: Unspecified error.
      virtual Sint32 remove_transaction_callback( IServer_transaction_callback* callback) = 0;
  
      /// Creates a snapshot of some database elements.
      ///
      /// The snapshot will save the provided element and all elements it references in their current
      /// state into the disk cache. The snapshot will only reference data already in the disk cache
      /// so storage cost of snapshots is very low. Data in the disk cache is guaranteed not to be
      /// removed for as long as there exists at least one snapshot that references the data.
      ///
      /// \see #create_incremental_snapshot_context(),
      ///      #mi::bridge::IBridge_server::create_snapshot_context()
      ///
      /// \param element            The top level element to save the snapshot for.
      /// \param[out] snapshot_id   The ID of the snapshot will be stored here if the operation is
      ///                           successful.
      /// \return
      ///                           -     0: Success.
      ///                           -    -1: Invalid arguments (\p element or \p snapshot_id is
      ///                                    \c NULL).
      ///                           -    -2: The specified element does not exist.
      ///                           -    -3: Failed to create the snapshot.
      ///                           - <= -4: Unspecified error.
      virtual Sint32 create_snapshot( const char* element, IString* snapshot_id) = 0;
  
      /// Creates a \em base snapshot and a context for subsequent incremental snapshots.
      ///
      /// In contrast to regular snapshots, incremental snapshots contain only those elements that
      /// have been changed since the last incremental snapshot of the same context. The first
      /// incremental snapshot, also called \em base snapshot, of a given context contains all
      /// elements, just as the regular snapshot.
      ///
      /// Incremental snapshots must be imported in the same order as they were created, starting with
      /// the base snapshot.
      ///
      /// \note The current transaction is only used for the base snapshot, the context itself is not
      ///       bound to this transaction.
      ///
      /// \see #create_snapshot(),
      ///      #mi::bridge::IBridge_server::create_snapshot_context()
      ///
      /// \param element            The top level element that incremental snapshots will be created
      ///                           for.
      /// \param[out] snapshot_id   The ID of the snapshot will be stored here if the operation is
      ///                           successful.
      /// \param[out] context       The context to be used for incremental snapshots.
      /// \return
      ///                           -     0: Success.
      ///                           -    -1: Invalid parameters (\p element, \p snapshot_id, or
      ///                                    \p context is \c NULL).
      ///                           -    -2: The specified element does not exist.
      ///                           -    -3: Failed to create the snapshot.
      ///                           - <= -4: Unspecified error.
      virtual Sint32 create_incremental_snapshot_context(
          const char* element, IString* snapshot_id, IIncremental_snapshot_context** context) = 0;
  
      /// Returns the number of database elements updated by this transaction so far.
      ///
      /// This number includes elements that have just been uploaded to the disk cache as well as
      /// elements that also have been replicated in the database.
      virtual Size get_updated_element_count() const = 0;
  
      /// Returns the list of currently pending jobs for this transaction in the order they will 
      /// be executed. Note that jobs added after the call to this method won't be present in the 
      /// list and that jobs might have completed execution when this list is processed.
      /// Also note that jobs within this transaction will execute in sequence but jobs belonging 
      /// to other transactions might execute in parallel if the transactions execute in parallel.
      ///
      /// Two transactions will execute in sequence if the second transaction is started after  
      /// the previous transaction was committed. Example: start A, commit A, start B commit B. 
      /// They will execute in parallel if the second transaction is started before the previous 
      /// transaction is committed. For instance start A, start B, commit A, commit B.
      ///
      /// \return A list of pending jobs in the order they will be executed.
      virtual IServer_job_list* get_pending_jobs() const = 0;
  
      /// Returns the id of this Bridge transaction. The id is only guaranteed to be unique among 
      /// the currently active transactions and is suitable only for log messages and the like. 
      /// Note that the Bridge transaction id is not the same as the id of the database transaction
      /// returned by get_database_transaction().
      virtual const char* get_id() const = 0;
  };
  
  /// The different states a server session can be in.
  ///
  /// \see #mi::bridge::IServer_session::get_state()
  enum Server_session_state
  {
      /// A client has connected to the application but has not yet been approved.
      SERVER_SESSION_CONNECTING = 0,
  
      /// The session has successfully established a connection to the Bridge application.
      SERVER_SESSION_CONNECTED  = 1,
  
      /// The session was disconnected unexpectedly and is waiting for the client to reconnect.
      SERVER_SESSION_PENDING    = 2,
  
      /// The session has been closed.
      SERVER_SESSION_CLOSED     = 3,
  
      SERVER_SESSION_FORCE_32_BIT = 0xffffffffU
  };
  
  mi_static_assert( sizeof( Server_session_state) == sizeof( Uint32));
  
  /// Abstract interface for callbacks for session state changes.
  ///
  /// \see #mi::bridge::IServer_session::add_session_state_callback(),
  ///      #mi::bridge::IServer_session::remove_session_state_callback()
  class IServer_session_state_callback : public
      mi::base::Interface_declare<0x12a50ba1,0x9cfc,0x4a12,0x9a,0x4b,0x52,0x13,0xf6,0x98,0x81,0x11>
  {
  public:
      /// This method is called whenever the session changes its state.
      virtual void session_state_callback( IServer_session* session) = 0;
  };
  
  /// A list of IServer_transaction instances.
  class IServer_transaction_list : public
      mi::base::Interface_declare<0xe97c6925,0x780,0x40b7,0x8c,0xc0,0xa9,0x2f,0x73,0xfe,0xb7,0x8
  >
  {
  public:
  
      /// Returns the number of transactions in the list.
      virtual Size get_size() = 0;
  
      /// Returns the job at the given index, or 0 if the index is out of bounds.
      virtual IServer_transaction* get_transaction(Size index) = 0;
  };
  
  /// Represents the server side of a Bridge session.
  ///
  /// \see #mi::bridge::IServer_session_state_callback::session_state_callback()
  ///      #mi::bridge::IApplication_session_handler::on_session_connect()
  class IServer_session : public
      mi::base::Interface_declare<0x42574f4a,0xfab1,0x4fdc,0xa0,0xc7,0x52,0x48,0xba,0xfa,0x8e,0x7d>
  {
  public:
      /// Returns the state of this session.
      virtual Server_session_state get_state() const = 0;
  
      /// Returns the application this session belongs to.
      virtual IApplication* get_application() const = 0;
  
      /// Returns the video context for a given ID.
      ///
      /// Video contexts must first be created by the client and can then be retrieved on the server
      /// via this method. The video context ID is assigned when the video context is created on the
      /// client side and must be transferred to the server before calling this method.
      /// The recommended way to do this is to execute a Bridge job containing the video context ID
      /// and optionally other data that is required for the server-side application to set up
      /// the video source and to start producing frames.
      ///
      /// \param context_id   The video context ID.
      /// \return             The video context or \c NULL if no video context with the provided ID
      /// exists.
      virtual IServer_video_context* get_video_context( Sint32 context_id) = 0;
  
      /// Adds a session state callback.
      ///
      /// When adding a callback it will be called immediately once with the current state, and then
      /// every time the session state changes.
      ///
      /// \see #remove_session_state_callback()
      ///
      /// \param callback    The callback to be added.
      virtual void add_session_state_callback( IServer_session_state_callback* callback) = 0;
  
      /// Removes a previously added session state callback.
      ///
      /// \see #add_session_state_callback()
      ///
      /// \param callback    The callback to be removed.
      virtual void remove_session_state_callback( IServer_session_state_callback* callback) = 0;
  
      /// Returns the security token specified by the client.
      virtual const char* get_security_token() const = 0;
  
      /// Returns the session ID.
      virtual const char* get_session_id() const = 0;
  
      /// Returns build number of the client.
      virtual const char* get_client_build_number() = 0;
  
      /// Returns the client Bridge protocol version. This is usually the same as the server 
      /// Bridge protocol version, but can differ if the server for instance accepts older 
      /// clients.
      virtual const char* get_client_bridge_protocol_version() = 0;
  
      /// Returns a list of the currently pending Bridge transactions for this session in the
      /// order they were started on the client. This is also the order the transactions will
      /// be started on the server, but some transactions in the list might not have been 
      /// started yet on the server which means there won't be a database transaction yet.
      ///
      /// Note that even though transactions will be started in the order specified by this 
      /// list some transactions will execute in sequence and others in parallel, depending 
      /// on how the transactions were started on the client.
      ///
      /// Also note that even though all transactions in the list were pending 
      /// when this call was made they might have been already committed or aborted when the 
      /// list is processed and that any transactions added after this call won't be present 
      /// in the list.
      ///
      /// Two transactions will execute in sequence if the second transaction is started after  
      /// the previous transaction was committed. Example: start A, commit A, start B commit B. 
      /// They will execute in parallel if the second transaction is started before the previous 
      /// transaction is committed. For instance start A, start B, commit A, commit B.
      ///
      /// \return A list of pending transactions in the order they were started on the client.
      virtual IServer_transaction_list* get_pending_transactions() = 0;
  };
  
  /// Abstract interface that can be used to control which sessions to a Bridge application are
  /// established, based on credentials supplied by the client.
  ///
  /// \see #mi::bridge::IApplication::set_session_handler()
  class IApplication_session_handler : public
      mi::base::Interface_declare<0x8913c078,0xeba2,0x4e0b,0x83,0x44,0x1,0xcb,0x2b,0x57,0x67,0x3c>
  {
  public:
      /// This method is called when a session to a Bridge application is to be established.
      ///
      /// Use #mi::bridge::IServer_session::get_security_token() to obtain the security token string
      /// supplied by the client when connecting. This token can be used to decide whether the client
      /// is allowed to connect.
      ///
      /// \param session   The session that is to be established.
      /// \return          \c true to accept the session, \c false to reject it.
      virtual bool on_session_connect( IServer_session* session) = 0;
  };
  
  /// This class represents a Bridge application.
  ///
  /// When a client opens a session to a Bridge server it also specifies the application it wants to
  /// connect to. The features of the application are defined by the Bridge jobs registered with the
  /// application.
  ///
  /// \see #mi::bridge::IBridge_server::create_application()
  class IApplication : public
      mi::base::Interface_declare<0x84c2d806,0x6e1f,0x402d,0xb2,0xa,0x2f,0xcf,0x47,0xd1,0xf,0x2e>
  {
  public:
      /// Registers a Bridge job with the application.
      ///
      /// All jobs to be executed via the Bridge need to be registered with the corresponding
      /// application.
      ///
      /// \see #unregister_job()
      ///
      /// \param job_class_id   The class ID of the job. You can simply pass IID() of your class
      ///                       derived from #mi::bridge::IServer_job.
      /// \param factory        The factory method of the job.
      /// \return               \c 0 in case of success, \c <0 in case of failure.
      virtual Sint32 register_job(
          const base::Uuid& job_class_id, neuraylib::IUser_class_factory* factory) = 0;
  
      /// Registers a Bridge job with the application.
      ///
      /// All jobs to be executed via the Bridge need to be registered with the corresponding
      /// application.
      ///
      /// This templated member function is a wrapper of the non-template variant for the user's
      /// convenience. It uses the default class factory #mi::neuraylib::User_class_factory
      /// specialized for T.
      ///
      /// \see #unregister_job()
      ///
      /// \return               \c 0 in case of success, \c <0 in case of failure.
      template <class T>
      Sint32 register_job()
      {
          mi::base::Handle<mi::neuraylib::IUser_class_factory> factory(
              new neuraylib::User_class_factory<T>());
          return register_job( typename T::IID(), factory.get());
      }
  
      /// Unregisters a Bridge job with the application.
      ///
      /// \see #register_job()
      ///
      /// \param job_class_id   The class id of the job.
      /// \return               \c 0 in case of success, \c <0 in case of failure.
      virtual Sint32 unregister_job( const base::Uuid& job_class_id) = 0;
  
      /// Unregisters a Bridge job with the application.
      ///
      /// This templated member function is a wrapper of the non-template variant for the user's
      /// convenience.
      ///
      /// \see #register_job()
      ///
      /// \return               \c 0 in case of success, \c <0 in case of failure.
      template <class T>
      Sint32 unregister_job()
      {
          return unregister_job( typename T::IID());
      }
  
      /// Sets the disk cache to use.
      ///
      /// \note The disk cache must be set before the application can be opened for client sessions.
      ///       The disk cache can not be changed after it has been successfully set.
      ///
      /// \see #get_disk_cache(), #mi::neuraylib::ICache_manager_factory
      ///
      /// \param location   The location of the disk cache. This can be either a directory on the
      ///                   local machine (prefix \c "path:") or the address of some cache manager
      ///                   (prefix \c "address:").
      /// \return
      ///                   -     0: Success.
      ///                   -    -1: Invalid argument (\p disk_cache is \c NULL or has an incorrect
      ///                            format).
      ///                   -    -2: No disk cache found at the specified location.
      ///                   -    -3: The disk cache was already set.
      ///                   - <= -4: Unspecified error.
      virtual Sint32 set_disk_cache( const char* location) = 0;
  
      /// Returns the disk cache location.
      ///
      /// \see #set_disk_cache()
      virtual const char* get_disk_cache() const = 0;
  
      /// Opens the application so that clients can open sessions to it.
      ///
      /// \return
      ///           -     0: Success
      ///           -    -1: Mandatory configuration is incomplete (\see #set_disk_cache()).
      ///           -    -2: The application is already open.
      ///           - <= -3: Unspecified error.
      virtual Sint32 open() = 0;
  
      /// Closes the application and any open sessions.
      ///
      /// \return
      ///          -     0: Success.
      ///          -    -1: The application is already closed.
      ///          - <= -2: Unspecified error.
      virtual Sint32 close() = 0;
  
      /// Indicates whether the application is open, i.e., whether it accepts client sessions.
      virtual bool is_open() = 0;
  
      /// Sets the session handler that will be called when clients connect.
      ///
      /// The session handler will be called as part of the handshake between client and server to
      /// decide whether to accept or to reject the client, e.g., based on a security token supplied
      /// by the client. The server will always accept clients if no session handler is set (default
      /// behavior). The session handler can also be used to keep track of which sessions are
      /// connected to the application and their state by adding an
      /// #mi::bridge::IServer_session_state_callback to the session.
      ///
      /// \param handler The handler to be set or \c NULL to remove the current handler.
      virtual Sint32 set_session_handler( IApplication_session_handler* handler) = 0;
  
      /// Returns the current session handler, or \c NULL if no session handler has been set.
      virtual IApplication_session_handler* get_session_handler() const = 0;
  
      /// Sets the maximum verbosity of log messages that will be forwarded to the client.
      ///
      /// Note that the log level that will be forwarded is requested by the client, and this setting
      /// will only override the client request in case the server needs to restrict logging further,
      /// for instance because of security considerations. So if the client requests that info level
      /// messages and more severe will be forwarded and the server sets the limit to warning
      /// severity, then only warning log messages will be forwarded.
      ///
      /// Defaults to #mi::base::MESSAGE_SEVERITY_DEBUG which will allow the client to request
      /// forwarding of all log messages. Set to #mi::base::MESSAGE_SEVERITY_FATAL to turn of log
      /// forwarding.
      ///
      /// \see #get_log_forwarding_limit()
      ///
      /// \param limit    The log message limit to set.
      virtual Sint32 set_log_forwarding_limit( base::Message_severity limit) = 0;
  
      /// Returns the maximum verbosity of log messages that will be forwarded to the client.
      ///
      /// \see #set_log_forwarding_limit()
      ///
      /// \return         The current log forwarding limit
      virtual base::Message_severity get_log_forwarding_limit() const = 0;
  };
  
  /// Context to import, export, or remove snapshots.
  ///
  /// \see #mi::bridge::IBridge_server::create_snapshot_context()
  class IBridge_snapshot_context : public
      mi::base::Interface_declare<0x9a9ceafe,0x876b,0x4647,0xbc,0xae,0xb6,0x4,0x9c,0x7b,0x9,0xfc>
  {
  public:
      /// Imports a previously saved snapshot from the disk cache.
      ///
      /// \if IRAY_API This method is similar to using #mi::neuraylib::IImport_api::import_elements(),
      /// but will load a previously saved snapshot from the disk cache. \endif
      ///
      /// In the case of incremental snapshots the base snapshot must be loaded first, and then each
      /// incremental snapshot in sequence.
      /// 
      /// Currently no importer options are supported.
      ///
      /// \param transaction        The transaction into which to import the elements.
      /// \param snapshot_id        The ID of the snapshot to import.
      /// \param importer_options   Optional importer options.
      /// \return                   Result of the import operation with the following error codes:
      ///                           -       0: Success.
      ///                           -    4001: Invalid parameters.
      ///                           -    4002: Failed to open the disk cache.
      ///                           -    4003: There is no snapshot with the given ID in this disk
      ///                                      cache.
      ///                           -    4004: Snapshot has an incompatible Bridge version.
      ///                           -    4005: Failed to import the snapshot elements.
      ///                           - >= 4006: Unspecified error.
      virtual neuraylib::IImport_result* import_snapshot(
          neuraylib::ITransaction* transaction,
          const char* snapshot_id,
          const IMap* importer_options = 0) = 0;
  
      /// Removes a snapshot from the disk cache.
      ///
      /// \param snapshot_id        The ID of the snapshot to remove.
      /// \return
      ///                           -     0: Success.
      ///                           -    -1: Invalid parameters.
      ///                           -    -2: Failed to open the disk cache.
      ///                           -    -3: There is no snapshot with the given ID in this disk
      ///                                    cache.
      ///                           - <= -4: Unspecified error
      virtual Sint32 remove_snapshot( const char* snapshot_id) = 0;
  
      /// Exports a set of elements to the disk cache.
      ///
      /// \if IRAY_API This method is similar to using #mi::neuraylib::IExport_api::export_elements(),
      /// but will save a snapshot in the disk cache specified. \endif
      ///
      /// The following optional exporter options are supported:
      /// - \c "recurse" of type #mi::IBoolean: If \c true, any elements referenced by the elements
      ///   in the \p elements array are exported as well. Default: true.
      ///
      /// \param transaction         The transaction to use for the export.
      /// \param elements            The elements to export. Only supports element names as IString.
      /// \param exporter_options    The exporter options.
      /// \param[in,out] snapshot_id The ID of the snapshot to use. If an empty string is passed an 
      ///                            automatically generated unique identifier will be assigned to 
      ///                            this string.
      /// \return                    Result of the export operation with the following error codes:
      ///                            -       0: Success.
      ///                            -    6001: Invalid parameters.
      ///                            -    6002: Disk cache IO error.
      ///                            -    6003: Failed to serialize a database element.
      ///                            -    6004: Failed to export snapshot.
      ///                            - >= 6005: Unspecified error.
      virtual neuraylib::IExport_result* export_snapshot(
          neuraylib::ITransaction* transaction,
          const IArray* elements,
          const IMap* exporter_options,
          IString* snapshot_id) = 0;
  };
  
  /// API component that serves as entry point for the server-side Bridge API.
  ///
  /// Can be used to create Bridge application and a context to import or remove snapshots.
  class IBridge_server : public
      mi::base::Interface_declare<0x1fd8a3ac,0xa70c,0x4273,0xa9,0x1a,0x67,0x57,0xdf,0xc7,0xa5,0xb>
  {
  public:
      /// Creates a Bridge application.
      ///
      /// The application will listen on the provided path on the given HTTP server for clients. The
      /// application has to be opened for connections before clients can open a session to it.
      ///
      /// \see #mi::bridge::IBridge_client::get_session()
      ///
      /// \param application_path   The path component of the WebSocket URL to the application. This
      ///                           identifier allows to run different Bridge applications on the same
      ///                           HTTP server. Note that the application path must begin with a
      ///                           slash.
      /// \param http_server        The HTTP server instance that handles WebSocket requests for
      ///                           this application.
      /// \return                   The created Bridge application, or \c NULL in case of failure
      ///                           (invalid arguments, \p application_path does not start with a
      ///                           slash, or an application for these arguments exists already).
      virtual IApplication* create_application(
          const char* application_path, http::IServer* http_server) = 0;
  
      /// Creates a snapshot context for importing or deleting snapshots.
      ///
      /// \see #mi::bridge::IServer_transaction::create_snapshot(),
      ///      #mi::bridge::IServer_transaction::create_incremental_snapshot_context()
      ///
      /// \param disk_cache    The location of the disk cache. This can be either a directory on the
      ///                      local machine (prefix \c "path:") or the address of some cache manager
      ///                      (prefix \c "address:").
      /// \return              The snapshot context.
      virtual IBridge_snapshot_context* create_snapshot_context( const char* disk_cache) = 0;
  
      /// Returns the Bridge protocol version string.
      virtual const char* get_bridge_protocol_version() const = 0;
  
      /// Returns the hash and serialized size for the provided element.
      ///
      /// \param transaction   The transaction to use when looking up the element.
      /// \param element       The name of the element.
      /// \param o_hash        The hash will be written to this IString on success.
      /// \param o_size        The serialized size in bytes will be assigned to this variable on
      ///                      success.
      /// \return
      ///                      -     0: Success.
      ///                      -    -1: Invalid arguments.
      ///                      -    -2: Failed to look up element with the provided name.
      ///                      - <= -2: Unspecified error.
      virtual Sint32 calculate_hash(
          neuraylib::ITransaction* transaction,
          const char* element,
          IString* o_hash,
          Size* o_size) = 0;
  };
  
  /*@}*/ // end group mi_neuray_bridge_server
  
  } // namespace bridge
  
  } // namespace mi
  
  #endif // MI_NEURAYLIB_IBRIDGE_SERVER_H
  