
com.twitter.finagle.Service.scala Maven / Gradle / Ivy
package com.twitter.finagle
import com.twitter.finagle.util.InetSocketAddressUtil.unconnected
import com.twitter.util.{Closable, Future, NonFatal, Time}
import java.net.SocketAddress
object Service {
/**
* Wrap the given service such that any synchronously thrown `NonFatal`
* exceptions are lifted into `Future.exceptions`.
*/
def rescue[Req, Rep](service: Service[Req, Rep]) = new ServiceProxy[Req, Rep](service) {
override def apply(request: Req): Future[Rep] = {
try {
service(request)
} catch {
case NonFatal(e) => Future.exception(e)
}
}
}
/**
* A convenience method for creating `Services` from a `Function1` of
* `Req` to a `Future[Rep]`.
*/
def mk[Req, Rep](f: Req => Future[Rep]): Service[Req, Rep] = new Service[Req, Rep] {
def apply(req: Req): Future[Rep] = f(req)
}
/**
* A service with a constant reply. Always available; never closable.
*
* @see [[constant]] for a Java compatible API.
*/
def const[Rep](rep: Future[Rep]): Service[Any, Rep] =
new service.ConstantService(rep)
/** Java compatible API for [[const]] as `const` is a reserved word in Java */
def constant[Rep](rep: Future[Rep]): Service[Any, Rep] =
Service.const(rep)
}
/**
* A `Service` is an asynchronous function from a `Request` to a `Future[Response]`.
*
* It is the basic unit of an RPC interface.
*
* @see The [[http://twitter.github.io/finagle/guide/ServicesAndFilters.html#services user guide]]
* for details and examples.
*
* @see [[com.twitter.finagle.Service.mk Service.mk]] for a convenient
* way to create new instances.
*/
abstract class Service[-Req, +Rep] extends (Req => Future[Rep]) with Closable {
def map[Req1](f: Req1 => Req) = new Service[Req1, Rep] {
def apply(req1: Req1): Future[Rep] = Service.this.apply(f(req1))
override def close(deadline: Time): Future[Unit] = Service.this.close(deadline)
}
/**
* This is the method to override/implement to create your own Service.
*/
def apply(request: Req): Future[Rep]
def close(deadline: Time): Future[Unit] = Future.Done
/**
* The current availability [[Status]] of this `Service`.
*/
def status: Status = Status.Open
/**
* Determines whether this `Service` is available (can accept requests
* with a reasonable likelihood of success).
*/
final def isAvailable: Boolean = status == Status.Open
}
/**
* Information about a client, passed to a Service factory for each new
* connection.
*/
trait ClientConnection extends Closable {
/**
* Host/port of the client. This is only available after `Service#connected`
* has been signalled.
*/
def remoteAddress: SocketAddress
/**
* Host/port of the local side of a client connection. This is only
* available after `Service#connected` has been signalled.
*/
def localAddress: SocketAddress
/**
* Expose a Future[Unit] that will be filled when the connection is closed
* Useful if you want to trigger action on connection closing
*/
def onClose: Future[Unit]
}
object ClientConnection {
val nil: ClientConnection = new ClientConnection {
def remoteAddress: SocketAddress = unconnected
def localAddress: SocketAddress = unconnected
def close(deadline: Time): Future[Unit] = Future.Done
def onClose: Future[Unit] = Future.never
}
}
/**
* A simple proxy Service that forwards all calls to another Service.
* This is useful if you want to wrap-but-modify an existing service.
*/
abstract class ServiceProxy[-Req, +Rep](val self: Service[Req, Rep])
extends Service[Req, Rep] with Proxy
{
def apply(request: Req): Future[Rep] = self(request)
override def close(deadline: Time): Future[Unit] = self.close(deadline)
override def status: Status = self.status
override def toString: String = self.toString
}
abstract class ServiceFactory[-Req, +Rep]
extends (ClientConnection => Future[Service[Req, Rep]])
with Closable
{ self =>
/**
* Reserve the use of a given service instance. This pins the
* underlying channel and the returned service has exclusive use of
* its underlying connection. To relinquish the use of the reserved
* Service, the user must call Service.close().
*/
def apply(conn: ClientConnection): Future[Service[Req, Rep]]
final def apply(): Future[Service[Req, Rep]] = this(ClientConnection.nil)
/**
* Apply `f` on created services, returning the resulting Future in their
* stead. This is useful for implementing common factory wrappers that
* only need to modify or operate on the underlying service.
*/
def flatMap[Req1, Rep1](f: Service[Req, Rep] => Future[Service[Req1, Rep1]]): ServiceFactory[Req1, Rep1] =
new ServiceFactory[Req1, Rep1] {
def apply(conn: ClientConnection): Future[Service[Req1, Rep1]] =
self(conn) flatMap { service =>
f(service) onFailure { _ => service.close() }
}
def close(deadline: Time) = self.close(deadline)
override def status: Status = self.status
override def toString(): String = self.toString()
}
/**
* Map created services. Useful for implementing common
* styles of factory wrappers.
*/
def map[Req1, Rep1](f: Service[Req, Rep] => Service[Req1, Rep1]): ServiceFactory[Req1, Rep1] =
flatMap { s => Future.value(f(s)) }
/**
* Make a service that after dispatching a request on that service,
* releases the service.
*/
final def toService: Service[Req, Rep] = new FactoryToService(this)
/**
* The current availability [[Status]] of this ServiceFactory
*/
def status: Status = Status.Open
final def isAvailable: Boolean = status == Status.Open
}
object ServiceFactory {
def const[Req, Rep](service: Service[Req, Rep]): ServiceFactory[Req, Rep] =
new ServiceFactory[Req, Rep] {
private[this] val noRelease = Future.value(new ServiceProxy[Req, Rep](service) {
// close() is meaningless on connectionless services.
override def close(deadline: Time) = Future.Done
})
def apply(conn: ClientConnection): Future[Service[Req, Rep]] = noRelease
def close(deadline: Time): Future[Unit] = Future.Done
}
def apply[Req, Rep](f: () => Future[Service[Req, Rep]]): ServiceFactory[Req, Rep] =
new ServiceFactory[Req, Rep] {
def apply(_conn: ClientConnection): Future[Service[Req, Rep]] = f()
def close(deadline: Time): Future[Unit] = Future.Done
}
}
/**
* A [[ServiceFactory]] that proxies all calls to another
* ServiceFactory. This can be useful if you want to modify
* and existing `ServiceFactory`.
*/
abstract class ServiceFactoryProxy[-Req, +Rep](_self: ServiceFactory[Req, Rep])
extends ServiceFactory[Req, Rep]
with Proxy {
def self: ServiceFactory[Req, Rep] = _self
def apply(conn: ClientConnection): Future[Service[Req, Rep]] = self(conn)
def close(deadline: Time): Future[Unit] = self.close(deadline)
override def status: Status = self.status
}
object FactoryToService {
val role = Stack.Role("FactoryToService")
// TODO: we should simply transform the stack for boolean
// stackables like this.
case class Enabled(enabled: Boolean) {
def mk(): (Enabled, Stack.Param[Enabled]) =
(this, Enabled.param)
}
object Enabled {
implicit val param = Stack.Param(Enabled(false))
}
/**
* Creates a [[com.twitter.finagle.Stackable]]
* [[FactoryToService]]. This makes per-request service acquisition
* part of the stack so it can be wrapped by filters such as tracing.
*/
def module[Req, Rep]: Stackable[ServiceFactory[Req, Rep]] =
new Stack.Module1[Enabled, ServiceFactory[Req, Rep]] {
val role = FactoryToService.role
val description = "Apply service factory on each service request"
def make(_enabled: Enabled, next: ServiceFactory[Req, Rep]) = {
if (_enabled.enabled) {
/*
* The idea here is to push FactoryToService down the stack
* so that service acquisition in the course of a request is
* wrapped by tracing, timeouts, etc. for that request.
*
* We can't return a Service directly (since the stack type
* is ServiceFactory); instead we wrap it in a
* ServiceFactoryProxy which returns the singleton Service.
* An outer FactoryToService (wrapping the whole stack)
* unwraps it.
*
* This outer FactoryToService also closes the service after
* each request, but we don't want to close the singleton,
* since it is itself a FactoryToService, so closing it
* closes the underlying factory; thus we wrap the service
* in a proxy which ignores the close.
*
* The underlying services are still closed by the inner
* FactoryToService, and the underlying factory is still
* closed when close is called on the outer FactoryToService.
*
* This is too complicated.
*/
val service = Future.value(new ServiceProxy[Req, Rep](new FactoryToService(next)) {
override def close(deadline: Time): Future[Unit] = Future.Done
})
new ServiceFactoryProxy(next) {
override def apply(conn: ClientConnection): Future[ServiceProxy[Req, Rep]] = service
}
} else {
next
}
}
}
}
/**
* Turns a [[com.twitter.finagle.ServiceFactory]] into a
* [[com.twitter.finagle.Service]] which acquires a new service for
* each request.
*/
class FactoryToService[Req, Rep](factory: ServiceFactory[Req, Rep])
extends Service[Req, Rep]
{
def apply(request: Req): Future[Rep] =
factory().flatMap { service =>
service(request).ensure {
service.close()
}
}
override def close(deadline: Time): Future[Unit] = factory.close(deadline)
override def status: Status = factory.status
}
/**
* A ServiceFactoryWrapper adds behavior to an underlying ServiceFactory.
*/
trait ServiceFactoryWrapper {
def andThen[Req, Rep](factory: ServiceFactory[Req, Rep]): ServiceFactory[Req, Rep]
}
object ServiceFactoryWrapper {
val identity: ServiceFactoryWrapper = new ServiceFactoryWrapper {
def andThen[Req, Rep](factory: ServiceFactory[Req, Rep]): ServiceFactory[Req, Rep] = factory
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy