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

net.liftmodules.amqp.AMQPDispatcher.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2007-2016 WorldWide Conferencing, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.liftmodules
package amqp

import _root_.com.rabbitmq.client._
import _root_.net.liftweb.actor._
import _root_.java.io.ObjectInputStream
import _root_.java.io.ByteArrayInputStream
import _root_.java.util.Timer
import _root_.java.util.TimerTask
import java.util.concurrent.ExecutorService

/**
 * @param a The actor to add as a Listener to this Dispatcher.
 */
case class AMQPAddListener(a: LiftActor)

/**
 * @param message A deserialized value received via AMQP.
 *
 * Messages received from AMQP are wrapped in this case class. When you
 * register a listener, this is the case class that you will be matching on.
 */
case class AMQPMessage[T](message: T)


/**
 * Reconnect to the AMQP Server after a delay of {@code delay} milliseconds.
 */
case class AMQPReconnect(delay: Long)

/**
 * An actor that serves as an endpoint for AMQP messages of serialized type T
 * coming into a specific queue/exchange.
 *
 * To listen for messages coming into that queue/exchange, send
 * this actor an AMQPAddListener message.
 *
 * For each message containing a value of type T, all listeners will be send
 * an AMQPMessage contaning that value.
 *
 * See also Enterprise Integration Patterns pp. 508-514
 *
 * @author Steve Jenson ([email protected])
 */
abstract class AMQPDispatcher[T](cf: ConnectionFactory)(implicit executor: ExecutorService = null) extends LiftActor {
  var (conn, channel) = connect()
  private var as: List[LiftActor] = Nil

  private def connect(): (Connection, Channel) = {
    val conn = cf.newConnection(executor)
    val channel = conn.createChannel()
    configure(channel)
    (conn, channel)
  }

  /**
   * Override this to configure the Channel and Consumer.
   */
  def configure(channel: Channel)
  
  private val reconnectTimer = new Timer("AMQPReconnectTimer")
  
  protected def messageHandler = {
    case AMQPAddListener(a) => as ::= a
    case msg@AMQPMessage(t) => as.foreach(_ ! msg)
    case AMQPReconnect(delay: Long) => 
      try {
        val details = connect()
        conn = details._1
        channel = details._2
        println("AMQPDispatcher: Successfully reconnected to AMQP Server")
      } catch {
        // Attempts to reconnect again using geometric back-off.
        case e: Exception => {
          val amqp = this
          println("AMQPDispatcher: Will attempt reconnect again in " + (delay * 2) + "ms.")
          reconnectTimer.schedule(new TimerTask(){
            override def run = {
              amqp ! AMQPReconnect(delay * 2)
            }
          }, delay)
        }
      }
      case _ => 
  }
}

/**
 * Example consumer on an AMQP channel.
 */
class SerializedConsumer[T](channel: Channel, a: LiftActor) extends DefaultConsumer(channel) {
  override def handleDelivery(tag: String, env: Envelope, props: AMQP.BasicProperties, body: Array[Byte]) {
    val routingKey = env.getRoutingKey
    val contentType = props.getContentType
    val deliveryTag = env.getDeliveryTag
    val in = new ObjectInputStream(new ByteArrayInputStream(body))
    val t = in.readObject.asInstanceOf[T];
    // Send t to all registered listeners.
    a ! AMQPMessage(t)
    channel.basicAck(deliveryTag, false);
  }
}

/**
 * Example Dispatcher that listens on an example queue and exchange. Use this
 * as your guiding example for creating your own Dispatcher.
 *
 */
class ExampleSerializedAMQPDispatcher[T](factory: ConnectionFactory)
    extends AMQPDispatcher[T](factory) {
  override def configure(channel: Channel) {
    // Set up the exchange and queue
    channel.exchangeDeclare("mult", "direct")
    val queue = channel.queueDeclare().getQueue
    channel.queueBind(queue, "mult", "routeroute")
    // Use the short version of the basicConsume method for convenience.
    channel.basicConsume("mult_queue", false, new SerializedConsumer(channel, this))
  }
}

/**
 * Example class that accepts Strings coming in from the
 * ExampleSerializedAMQPDispatcher.
 */
class ExampleStringAMQPListener {
  lazy val factory = new ConnectionFactory {
    import ConnectionFactory._

    // thor.local is a machine on your network with rabbitmq listening on port 5672
    setHost("localhost")
    setPort(DEFAULT_AMQP_PORT)
    setUsername(DEFAULT_USER)
    setPassword(DEFAULT_PASS)
    setVirtualHost(DEFAULT_VHOST)
  }

  // thor.local is a machine on your network with rabbitmq listening on port 5672
  val amqp = new ExampleSerializedAMQPDispatcher[String](factory)

  // Example Listener that just prints the String it receives.

  val stringListener = new LiftActor {
    protected def messageHandler = {
      case msg@AMQPMessage(contents: String) => println("received: " + msg)
    }
  }
  
  amqp ! AMQPAddListener(stringListener)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy