com.twitter.finagle.Service.scala Maven / Gradle / Ivy
The newest version!
package com.twitter.finagle
import java.net.SocketAddress
import com.twitter.util.{Closable, Future, Time}
object Service {
/**
* Wrap an underlying service such that any synchronously thrown exceptions are lifted into
* Future.exception
*/
def rescue[Req, Rep](service: Service[Req, Rep]) = new ServiceProxy[Req, Rep](service) {
override def apply(request: Req) = {
try {
service(request)
} catch {
case e: Throwable => Future.exception(e)
}
}
}
def mk[Req, Rep](f: Req => Future[Rep]): Service[Req, Rep] = new Service[Req, Rep] {
def apply(req: Req) = f(req)
}
}
/**
* A Service is an asynchronous function from Request to Future[Response]. It is the
* basic unit of an RPC interface.
*
* '''Note:''' this is an abstract class (vs. a trait) to maintain java
* compatibility, as it has implementation as well as interface.
*/
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) = Service.this.apply(f(req1))
override def close(deadline: Time) = Service.this.close(deadline)
}
/**
* This is the method to override/implement to create your own Service.
*/
def apply(request: Req): Future[Rep]
/**
* Relinquishes the use of this service instance. Behavior is
* undefined if apply() is called after resources are relinquished.
*/
// This is asynchronous on purpose, the old API allowed for it.
@deprecated("Use close() instead", "7.0.0")
final def release() { close() }
def close(deadline: Time) = Future.Done
/**
* Determines whether this service is available (can accept requests
* with a reasonable likelihood of success).
*/
def isAvailable: Boolean = true
}
/**
* 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 {
private[this] val unconnected =
new SocketAddress { override def toString = "unconnected" }
def remoteAddress = unconnected
def localAddress = unconnected
def close(deadline: Time) = Future.Done
def onClose = 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) = self(request)
override def close(deadline: Time) = self.close(deadline)
override def isAvailable = self.isAvailable
override def toString = 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)
@deprecated("use apply() instead", "5.0.1")
final def make(): Future[Service[Req, Rep]] = this()
/**
* 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) =
self(conn) flatMap { service =>
f(service) onFailure { _ => service.close() }
}
def close(deadline: Time) = self.close(deadline)
override def isAvailable = self.isAvailable
override def toString() = 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)
def isAvailable: Boolean = true
}
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) = noRelease
def close(deadline: Time) = Future.Done
}
def apply[Req, Rep](f: () => Future[Service[Req, Rep]]): ServiceFactory[Req, Rep] =
new ServiceFactory[Req, Rep] {
def apply(_conn: ClientConnection) = f()
def close(deadline: Time) = Future.Done
}
}
@deprecated("use ServiceFactoryProxy instead", "6.7.5")
trait ProxyServiceFactory[-Req, +Rep] extends ServiceFactory[Req, Rep] with Proxy {
def self: ServiceFactory[Req, Rep]
def apply(conn: ClientConnection) = self(conn)
def close(deadline: Time) = self.close(deadline)
override def isAvailable = self.isAvailable
}
/**
* A simple proxy ServiceFactory that forwards all calls to another
* ServiceFactory. This is is useful if you to wrap-but-modify an
* existing service factory.
*/
abstract class ServiceFactoryProxy[-Req, +Rep](_self: ServiceFactory[Req, Rep])
extends ProxyServiceFactory[Req, Rep] {
def self: ServiceFactory[Req, Rep] = _self
}
class FactoryToService[Req, Rep](factory: ServiceFactory[Req, Rep])
extends Service[Req, Rep]
{
def apply(request: Req) =
factory() flatMap { service =>
service(request) ensure {
service.close()
}
}
override def close(deadline: Time) = factory.close(deadline)
override def isAvailable = factory.isAvailable
}
/**
* 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 - 2024 Weber Informatics LLC | Privacy Policy