Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tag: heroku-devcent…
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 144 lines (128 sloc) 5.127 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
package com.typesafe.webwords

import java.net.URI
import java.net.URISyntaxException
import akka.actor.Channel
import akka.actor.ActorKilledException
import akka.actor.ActorRef
import akka.actor.LocalActorRef
import akka.dispatch.Future
import akka.dispatch.MessageInvocation
import akka.dispatch.MessageQueue
import akka.actor.Actor
import akka.dispatch.CompletableFuture
import akka.actor.ActorInitializationException
import akka.dispatch.DefaultCompletableFuture
import akka.dispatch.FutureTimeoutException
import akka.actor.NullChannel
import akka.actor.UntypedChannel

/**
* This file contains random utility functions.
* Some of them may be interesting.
*/
package object common {

    // Class that adds replyWith to Akka channels
    class EnhancedChannel[-T](underlying: Channel[T]) {
        /**
* Replies to a channel with the result or exception from
* the passed-in future
*/
        def replyWith[A <: T](f: Future[A])(implicit sender: UntypedChannel) = {
            f.onComplete({ f =>
                f.value.get match {
                    case Left(t) =>
                        underlying.sendException(t)
                    case Right(v) =>
                        underlying.tryTell(v)
                }
            })
        }
    }

    // implicitly create an EnhancedChannel wrapper to add methods to the
    // channel
    implicit def enhanceChannel[T](channel: Channel[T]): EnhancedChannel[T] = {
        new EnhancedChannel(channel)
    }

    private def getMailbox(self: ActorRef) = {
        self match {
            // LocalActorRef.mailbox is public but
            // ActorRef.mailbox is not; not sure if
            // it's deliberate or a bug, but we use it...
            // this code can be deleted with newer Akka versions
            // that have fix https://www.assembla.com/spaces/akka/tickets/894
            case local: LocalActorRef =>
                local.mailbox
            case _ =>
                throw new Exception("Can't get mailbox on this ActorRef: " + self)
        }
    }

    private def invocations(mq: MessageQueue): Stream[MessageInvocation] = {
        val mi = mq.dequeue
        if (mi eq null)
            Stream.empty
        else
            Stream.cons(mi, invocations(mq))
    }

    private def sendExceptionsToMailbox(mailbox: AnyRef) = {
        mailbox match {
            case mq: MessageQueue =>
                invocations(mq) foreach { mi =>
                    mi.channel.sendException(new ActorKilledException("Actor is about to suicide"))
                }
            case _ =>
                throw new Exception("Don't know how to iterate over mailbox: " + mailbox)
        }
    }

    // Akka 2.0 has a fix where, on stopping an actor,
    // the sender gets an exception;
    // see https://www.assembla.com/spaces/akka/tickets/894
    // In 1.2, we use this temporary workaround to simulate the 2.0 behavior.
    def stopActorNotifyingMailbox(self: ActorRef) = {
        val mailbox = getMailbox(self)
        self.stop
        sendExceptionsToMailbox(mailbox)
    }

    def tryAsk(actor: ActorRef, message: Any)(implicit channel: UntypedChannel = NullChannel, timeout: Actor.Timeout = Actor.defaultTimeout): CompletableFuture[Any] = {
        // "?" will throw by default on a stopped actor; we want to put an exception
        // in the future instead to avoid special cases
        try {
            actor ? message
        } catch {
            case e: ActorInitializationException =>
                val f = new DefaultCompletableFuture[Any]()
                f.completeWithException(new ActorKilledException("Actor was not running, immediate timeout"))
                f
        }
    }

    private def stripSlash(s: String) =
        if (s == "")
            null
        else if (s.startsWith("/"))
            s.substring(1)
        else
            s

    case class URIParts(scheme: String, user: Option[String], password: Option[String],
        host: Option[String], port: Option[Int], path: Option[String])

    def expandURI(s: String, defaults: URIParts): Option[URIParts] = {
        try {
            val uri = new URI(s)

            val host = Option(uri.getHost) orElse (defaults.host)
            val port = (if (uri.getPort == -1) None else Some(uri.getPort)) orElse (defaults.port)

            // URI has the "/" in front of the path but URIParts strips it off.
            val path = Option(stripSlash(uri.getPath)) orElse (defaults.path)
            val userInfo = Option(uri.getUserInfo)
            val (user, password) = userInfo map { ui =>
                if (ui.contains(":")) {
                    val a = ui.split(":", 2)
                    (Some(a(0)) -> Some(a(1)))
                } else {
                    (Some(ui) -> defaults.password)
                }
            } getOrElse (defaults.user -> defaults.password)

            Some(URIParts(scheme = uri.getScheme, user = user, password = password,
                host = host, port = port, path = path))
        } catch {
            case e: URISyntaxException =>
                None
        }
    }
}
Something went wrong with that request. Please try again.