All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.reactors.protocol.two-way-protocols.scala Maven / Gradle / Ivy

The newest version!
package io.reactors
package protocol



import io.reactors.services.Channels



trait TwoWayProtocols {
  /** Represents the state of an established 2-way channel.
   *
   *  To close the 2-way channel, use the associated `Subscription` object.
   *
   *  @tparam I              type of the incoming events
   *  @tparam O              type of the outgoing events
   *  @param output          the output channel, for outgoing events
   *  @param input           the input event stream, for incoming events
   *  @param subscription    subscription associated with this 2-way link
   */
  case class TwoWay[I, O](
    output: Channel[O], input: Events[I], subscription: Subscription
  ) extends Connection[I] {
    /** Same as `input`, events provided by this link.
     */
    def events = input

    /** Same as `output`, channel provided to write with this link.
     */
    def channel = output
  }

  object TwoWay {
    /** Tag for the server-side outgoing channels of the `TwoWay` object.
     */
    object OutputTag extends Channels.Tag

    /** Tag for the client-side outgoing channels of the `TwoWay` object.
     */
    object InputTag extends Channels.Tag

    /** Two-way server object.
     *
     *  @tparam I            type of the server-side input events
     *  @tparam O            type of the server-side output events
     *  @param channel       request channel for establishing 2-way links
     *  @param links         event stream that emits established links
     *  @param subscription  subscription for the server and its resources
     */
    case class Server[I, O](
      channel: io.reactors.protocol.Server[Channel[I], Channel[O]],
      links: Events[TwoWay[O, I]],
      subscription: Subscription
    ) extends ServerSide[Req[I, O], TwoWay[O, I]]

    /** Request object for 2-way server channels.
     *
     *  @tparam I            type of the server-side input events
     *  @tparam O            type of the server-side output events
     */
    type Req[I, O] = io.reactors.protocol.Server.Req[Channel[I], Channel[O]]
  }

  implicit class TwoWayChannelBuilderOps(val builder: ChannelBuilder) {
    /** Creates a connector for a 2-way server.
     *
     *  The connector is just a placeholder, but creating it does not yet run the
     *  2-way protocol. To start the 2-way protocol, users must call the `serverTwoWay`
     *  method.
     *
     *  @tparam I            type of the server-side input events
     *  @tparam O            type of the server-side output events
     *  @return              the server connector
     */
    def twoWayServer[
      @spec(Int, Long, Double) I, @spec(Int, Long, Double) O
    ]: Connector[TwoWay.Req[I, O]] = {
      builder.open[TwoWay.Req[I, O]]
    }
  }

  implicit class TwoWayConnectorOps[
    @spec(Int, Long, Double) I, @spec(Int, Long, Double) O
  ](val connector: Connector[TwoWay.Req[I, O]]) {
    /** Starts a 2-way server protocol.
     *
     *  This method can be called on a previously created 2-way server connector
     *  (see `twoWayServer`).
     */
    def serveTwoWay()(implicit a: Arrayable[O]): TwoWay.Server[I, O] = {
      val links = connector.events map {
        case (inputChannel, reply) =>
          val system = Reactor.self.system
          val output = system.channels.template(TwoWay.OutputTag).daemon.open[O]
          reply ! output.channel
          TwoWay(inputChannel, output.events, Subscription(output.seal()))
      } toEmpty

      TwoWay.Server(
        connector.channel,
        links,
        links.chain(Subscription(connector.seal()))
      )
    }
  }

  implicit class TwoWayServerOps[
    @spec(Int, Long, Double) I, @spec(Int, Long, Double) O
  ](val twoWayServer: Channel[TwoWay.Req[I, O]]) {
    /** Connects to a 2-way protocol server.
     *
     *  A successful link yields a 2-way channel object of type `TwoWay[I, O]`.
     */
    def connect()(
      implicit a: Arrayable[I]
    ): IVar[TwoWay[I, O]] = {
      val system = Reactor.self.system
      val input = system.channels.template(TwoWay.InputTag).daemon.open[I]
      val result: Events[TwoWay[I, O]] = (twoWayServer ? input.channel) map {
        outputChannel =>
        TwoWay(outputChannel, input.events, Subscription(input.seal()))
      }
      result.toIVar
    }
  }

  implicit class TwoWayReactorCompanionOps(val reactor: Reactor.type) {
    /** Create a `Proto` object for a 2-way server reactor.
     *
     *  @tparam I       type of the server-side input events
     *  @tparam O       type of the server-side output events
     *  @param f        callback function for a successful incoming link
     *  @return         a `Proto` object
     */
    def twoWayServer[@spec(Int, Long, Double) I, @spec(Int, Long, Double) O](
      f: (TwoWay.Server[I, O], TwoWay[O, I]) => Unit
    )(implicit ai: Arrayable[I], ao: Arrayable[O]): Proto[Reactor[TwoWay.Req[I, O]]] = {
      Reactor[TwoWay.Req[I, O]] { self =>
        val server = self.main.serveTwoWay()
        server.links.onEvent(twoWay => f(server, twoWay))
      }
    }
  }

  implicit class TwoWaySystemOps(val system: ReactorSystem) {
    /** Create a `Proto` object for a 2-way server reactor.
     *
     *  @tparam I       type of the server-side input events
     *  @tparam O       type of the server-side output events
     *  @param f        callback function for a successful incoming link
     *  @return         a channel of the 2-way server
     */
    def twoWayServer[@spec(Int, Long, Double) I, @spec(Int, Long, Double) O](
      f: (TwoWay.Server[I, O], TwoWay[O, I]) => Unit
    )(implicit ai: Arrayable[I], ao: Arrayable[O]): Channel[TwoWay.Req[I, O]] = {
      val proto = Reactor.twoWayServer(f)
      system.spawn(proto)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy