(* $Id: netplex_kit.mli 2195 2015-01-01 12:23:39Z gerd $ *)

(** Netplex toolkit *)

open Netplex_types


(** Same as [processor], but the methods [process] and [supported_ptypes]
  * are flagged as [virtual]
 *)
class type virtual v_processor =
object
  inherit processor_hooks

  method virtual process : 
           when_done:(unit -> unit) ->
           container -> Unix.file_descr -> string -> unit
  method virtual supported_ptypes : parallelization_type list
end


class empty_processor_hooks : unit -> processor_hooks
  (** This is an empty set of processor hooks, i.e. all methods are empty
   *)

class processor_hooks_delegation : processor_hooks -> processor_hooks
  (** Takes a hooks object, and makes a class of it. Useful for overriding
      methods in an object.
   *)

class virtual processor_base :  processor_hooks -> v_processor
  (** A virtual (incomplete) base class for processors. As argument the
    * user-supplied hooks are passed in. Use this class as in:
    *
    * {[
    *    class my_processor hooks =
    *    object(self)
    *      inherit Netplex_kit.processor_base hooks
    *      method process ~when_done container fd proto_name = ...
    *      method supported_ptypes = ...
    *    end
    * ]}
    *
    * In order to run actions from hooks, redefine the hook methods as in:
    *
    * {[
    *    class my_processor hooks =
    *    object(self)
    *      inherit Netplex_kit.processor_base hooks as super
    *      method process ~when_done container fd proto_name = ...
    *      method supported_ptypes = ...
    *      method post_start_hook container =
    *        ... (* my action *);
    *        super # post_start_hook container
    *    end
    * ]}
   *)

class protocol_switch_processor : (string * processor) list -> processor
  (** The arg is a list of pairs [(proto_name, proto_proc)]. All mentioned
      processors are merged into a single processor. When a TCP connection
      arrives, it depends on the protocol which processor is actually
      activated. (Every socket is bound to a protocol, so this can be derived
      from the socket.)

      It is up to the user whether the merge makes sense.
   *)

class protocol_switch_factory : string ->
                                (string * processor_factory) list ->
                                processor_factory
  (** [protocol_switch_factory name merge_list]: Merges the factories
      in [merge_list] to a single factory. Which factory is selected
      depends on the protocol.

      For example:

      {[
          service {
            name = "...";
            protocol {
               name = "A"; ...;
            }
            protocol {
               name = "B"; ...;
            }
            processor {
               type = "merged";
               A {
                  ...
               }
               B {
                  ...
               }
            }
          }
      ]}
                                  
      Here, two protocols [A] and [B] are defined, and there is a
      subsection in [processor] for each of the protocols configuring
      the used service. "merged" is the [name] of the merged factories.

      For example, [A] could be an RPC interface, and [B] could be
      an HTTP interface providing the same service.

      For every protocol in [merge_list] there must be a subsection in
      [processor] for the protocol. This subsection configures then
      the processor. It is not an error not to create sockets for
      a protocol in [merge_list].
   *)


val add_helper_service : controller -> string -> processor_hooks -> unit
  (** [add_helper_service ctrl name hooks]: Adds a helper service [name] to
      the controller
      [ctrl]. The helper service does not have any externally
      accessible socket, but starts a single regular container that looks
      like any other container. Whatever needs to be initialized must be
      done in the [pre_start_hook] or the [post_start_hook].

      This function must be called in controller context, for example
      in the [late_initializer] of {!Netplex_main.startup}, but it can
      also be started later.

      For an example, look at [examples/netplex/helper_container.ml] in
      the distributed source tarball.

      For multi-threaded programs, {!Netplex_cenv.run_in_controller_context}
      is the required companion function to start helper threads at any
      time. Multi-processing programs do not have such an easy way to 
      add helpers. They should it at program startup time.

      {b Known bug.} The the helper component will be in "starting" state as 
      long as the [post_start_hook] runs.
   *)

val create_protocol : ?lstn_backlog:int -> 
                      ?lstn_reuseaddr:bool ->
                      ?so_keepalive:bool -> 
                      ?tcp_nodelay:bool ->
                      ?local_chmod:int ->
                      ?local_chown:(int*int) ->
                      ?configure_slave_socket:(Unix.file_descr ->unit) ->
                      string ->
                      extended_address array ->
                        protocol
  (** [create_protocol name addresses]: Creates a [protocol] object
      from the passed arguments
   *)
			
val create_socket_service_config : 
                      ?startup_timeout:float ->
                      ?change_user_to:(int*int) ->
                      ?gc_when_idle:bool ->
                      ?conn_limit:int ->
                      string ->
                      protocol list ->
                      controller_config ->
                        socket_service_config
  (** [create_socket_service_config name protos ctrl_conf]: Creates a
      [socket_service_config] object from the passed arguments
   *)

