package net.shrine.adapter.service
import net.shrine.log.Loggable
import net.shrine.protocol.{BaseShrineResponse, BroadcastMessage, ErrorResponse, NodeId, RequestType, Result, Signature}
import net.shrine.adapter.AdapterMap
import net.shrine.crypto.Verifier
import net.shrine.problem.{AbstractProblem, ProblemSources}
import scala.concurrent.duration.Duration
import scala.concurrent.duration._
/**
* Heart of the adapter.
*
* @author clint
* @since Nov 14, 2013
*/
final class AdapterService(
nodeId: NodeId,
signatureVerifier: Verifier,
maxSignatureAge: Duration,
adapterMap: AdapterMap) extends AdapterRequestHandler with Loggable {
import AdapterService._
logStartup(adapterMap)
override def handleRequest(message: BroadcastMessage): Result = {
handleInvalidSignature(message).orElse {
for {
adapter <- adapterMap.adapterFor(message.request.requestType)
} yield time(nodeId) {
adapter.perform(message)
}
}.getOrElse {
Result(nodeId, 0.milliseconds, ErrorResponse(UnknownRequestType(message.request.requestType)))
}
}
/**
* @return None if the signature is fine, Some(result with an ErrorResponse) if not
*/
private def handleInvalidSignature(message: BroadcastMessage): Option[Result] = {
val (sigIsValid, elapsed) = time(signatureVerifier.verifySig(message, maxSignatureAge))
if(sigIsValid) { None }
else {
info(s"Incoming message had invalid signature: $message")
Some(Result(nodeId, elapsed.milliseconds, ErrorResponse(CouldNotVerifySignature(message))))
}
}
}
object AdapterService extends Loggable {
private def logStartup(adapterMap: AdapterMap) {
info("Adapter service initialized, will respond to the following queries: ")
val sortedByReqType = adapterMap.requestsToAdapters.toSeq.sortBy { case (k, _) => k }
sortedByReqType.foreach {
case (requestType, adapter) =>
info(s" $requestType:\t(${adapter.getClass.getSimpleName})")
}
}
private[service] def time[T](f: => T): (T, Long) = {
val start = System.currentTimeMillis
val result = f
val elapsed = System.currentTimeMillis - start
(result, elapsed)
}
private[service] def time(nodeId: NodeId)(f: => BaseShrineResponse): Result = {
val (response, elapsed) = time(f)
Result(nodeId, elapsed.milliseconds, response)
}
}
case class CouldNotVerifySignature(message: BroadcastMessage) extends AbstractProblem(ProblemSources.Adapter){
val signature: Option[Signature] = message.signature
override val summary: String = signature.fold("A message was not signed")(sig => s"The trust relationship with ${sig.signedBy} is not properly configured.")
override val description: String = signature.fold(s"The Adapter at ${stamp.host.getHostName} could not properly validate a request because it had no signature.")(sig => s"The Adapter at ${stamp.host.getHostName} could not properly validate the request from ${sig.signedBy}. An incoming message from the hub had an invalid signature.")
override val detailsXml = signature.fold(
)(
sig =>
Signature is {sig}
)
}
case class UnknownRequestType(requestType: RequestType) extends AbstractProblem(ProblemSources.Adapter){
override val summary: String = s"Unknown request type $requestType"
override val description: String = s"The Adapter at ${stamp.host.getHostName} received a request of type $requestType that it cannot process."
}