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

com.github.mitallast.nsq.NSQLookup.scala Maven / Gradle / Ivy

The newest version!
package com.github.mitallast.nsq

import java.io.InputStream
import java.net.{HttpURLConnection, InetSocketAddress, SocketAddress, URL}

import com.github.mitallast.nsq.protocol.JsonParser
import com.typesafe.config.{Config, ConfigFactory}
import org.slf4j.LoggerFactory

import scala.collection.JavaConversions._
import scala.io.Source

trait NSQLookup {

  def lookup(topic: String): List[SocketAddress]

  def nodes(): List[SocketAddress]
}

object NSQLookup {

  def apply(): NSQLookup = apply(ConfigFactory.load())

  def apply(config: Config): NSQLookup = apply(config.withFallback(ConfigFactory.defaultReference()).getStringList("nsq.lookup-address").toList)

  def apply(address: String): NSQLookup = apply(List(address))

  def apply(addresses: List[String]): NSQLookup = new NSQLookupDefault(addresses)
}

class NSQLookupDefault(addresses: List[String]) extends NSQLookup {
  private val log = LoggerFactory.getLogger(getClass)

  def lookup(topic: String): List[SocketAddress] = {
    addresses.flatMap { address ⇒
      try {
        val url = new URL(address)
        val lookupUrl = new URL(url.getProtocol, url.getHost, url.getPort, "/lookup?topic=" + topic)

        if (log.isDebugEnabled) {
          log.debug(s"lookup $url topic $topic")
        }
        val connection = lookupUrl.openConnection().asInstanceOf[HttpURLConnection]
        connection.setRequestMethod("GET")
        connection.connect()
        connection.getResponseCode match {
          case 200 ⇒
            val stream = connection.getInputStream
            try {
              parseResponse(stream)
            } finally {
              stream.close()
            }
          case _ ⇒
            List.empty
        }

      } catch {
        case e: Exception ⇒
          log.warn(s"failed lookup $address", e)
          List.empty
      }
    }.distinct
  }

  def nodes(): List[SocketAddress] = {
    addresses.flatMap { address ⇒
      try {

        val url = new URL(address)
        val lookupUrl = new URL(url.getProtocol, url.getHost, url.getPort, "/nodes")

        if (log.isDebugEnabled) {
          log.debug(s"lookup $url nodes")
        }
        val connection = lookupUrl.openConnection().asInstanceOf[HttpURLConnection]
        connection.setRequestMethod("GET")
        connection.connect()
        connection.getResponseCode match {
          case 200 ⇒
            val stream = connection.getInputStream
            try {
              parseResponse(stream)
            } finally {
              stream.close()
            }
          case _ ⇒
            List.empty
        }
      } catch {
        case e: Exception ⇒
          log.warn(s"failed lookup nodes", e)
          List.empty
      }
    }
  }

  private[nsq] def parseResponse(stream: InputStream): List[SocketAddress] = {
    import JsonParser._
    val json = Source.fromInputStream(stream).mkString
    val parser = new JsonParser(json)

    var addresses = List.empty[SocketAddress]

    def parseResponse(): Unit = {
      assert(parser.next() == startObject)
      while (true) {
        parser.next() match {
          case valueString("data") ⇒
            parseData()
          case valueString("producers") ⇒
            parseProducers()
          case valueString(field) ⇒
            parser.skipField()
          case JsonParser.endObject ⇒
            return
          case token ⇒ throw new RuntimeException("unexpected token: " + token)
        }
      }
    }

    def parseData(): Unit = {
      assert(parser.next() == startObject)
      while (true) {
        parser.next() match {
          case valueString("producers") ⇒
            parseProducers()
          case valueString(name) ⇒
            parser.skipField()
          case JsonParser.endObject ⇒
            return
          case token ⇒ throw new RuntimeException("unexpected token: " + token)
        }
      }
    }

    def parseProducers(): Unit = {
      assert(parser.next() == startArray)
      while (true) {
        parser.next() match {
          case JsonParser.startObject ⇒
            parseProducer()
          case JsonParser.endArray ⇒
            return
          case token ⇒ throw new RuntimeException("unexpected token: " + token)
        }
      }
    }

    def parseProducer(): Unit = {
      var hostname = ""
      var broadcast_address = ""
      var tcp_port = 4150

      while (true) {
        parser.next() match {
          case valueString("hostname") ⇒
            parser.next() match {
              case valueString(addr) ⇒
                hostname = addr
              case token ⇒ throw new RuntimeException("unexpected token: " + token)
            }
          case valueString("broadcast_address") ⇒
            parser.next() match {
              case valueString(addr) ⇒
                broadcast_address = addr
              case token ⇒ throw new RuntimeException("unexpected token: " + token)
            }
          case valueString("tcp_port") ⇒
            parser.next() match {
              case valueLong(port) ⇒
                tcp_port = port.toInt
              case token ⇒ throw new RuntimeException("unexpected token: " + token)
            }
          case valueString(name) ⇒
            parser.skipField()
          case JsonParser.endObject ⇒
            if (broadcast_address != "") {
              addresses = addresses ++ List(new InetSocketAddress(broadcast_address, tcp_port))
            } else if (hostname != "") {
              addresses = addresses ++ List(new InetSocketAddress(hostname, tcp_port))
            }
            return
          case token ⇒ throw new RuntimeException("unexpected token: " + token)
        }
      }
    }

    parseResponse()

    addresses
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy