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

com.mle.ws.SocketClient.scala Maven / Gradle / Ivy

The newest version!
package com.mle.ws

import java.net.URI
import javax.net.ssl.SSLContext

import com.mle.concurrent.{ExecutionContexts, Futures}
import org.java_websocket.client.{DefaultSSLWebSocketClientFactory, WebSocketClient}
import org.java_websocket.drafts.Draft_10
import org.java_websocket.handshake.ServerHandshake
import rx.lang.scala.{Observable, Subject}

import scala.collection.JavaConversions._
import scala.concurrent.duration.DurationInt
import scala.concurrent.{Future, Promise}
import scala.util.{Failure, Success, Try}

/**
 * @author Michael
 */
abstract class SocketClient[T](uri: String,
                               sslContext: Option[SSLContext],
                               additionalHeaders: (String, String)*) extends WebSocketBase[T] {
  protected val connectTimeout = 10.seconds
  protected val connectPromise = Promise[Unit]()
  protected val subject = Subject[T]()
  val headers: java.util.Map[String, String] = Map.empty[String, String] ++ additionalHeaders.toMap

  protected def parse(raw: String): Option[T]

  protected def stringify(message: T): String

  def messages: Observable[T] = subject

  val client = new WebSocketClient(URI create uri, new Draft_10, headers, 0) {
    def onOpen(handshakedata: ServerHandshake) {
      //      log info s"Opened websocket to: $uri"
      connectPromise.trySuccess(())
      //      subject onNext SocketConnected
    }

    def onMessage(message: String): Unit = {
      SocketClient.this.onRawMessage(message)
    }

    /**
     *
     * @param code 1000 if the client disconnects normally, 1006 if the server dies abnormally
     * @param reason
     * @param remote
     * @see http://tools.ietf.org/html/rfc6455#section-7.4.1
     */
    def onClose(code: Int, reason: String, remote: Boolean) {
      //      log info s"Closed websocket to: $uri, code: $code, reason: $reason, remote: $remote"
      connectPromise tryFailure new NotConnectedException(s"The websocket was closed. Code: $code, reason: $reason.")
      SocketClient.this.onClose()
      //      eventsSubject onNext Disconnected
      // not sure if I should do this; the connect() docs says it "maintains" a connection?
      subject.onCompleted()
    }

    /**
     * Exceptions thrown in this handler like in onMessage end up here.
     *
     * If the connection attempt fails, this is called with a [[java.net.ConnectException]].
     */
    def onError(ex: Exception) {
      //      log.warn("WebSocket error", ex)
      connectPromise tryFailure ex
      SocketClient.this.onError(ex)
      subject onError ex
    }
  }
  if (uri startsWith "wss") {
    sslContext.foreach(ctx => {
      val factory = new DefaultSSLWebSocketClientFactory(ctx)
      client setWebSocketFactory factory
    })
  }

  /**
   * Only call this method once per instance.
   *
   * Impl: On subsequent calls, the returned future will always be completed regardless of connection result
   *
   * @return a [[Future]] that completes successfully when the connection has been established or fails otherwise
   */
  def connect(): Future[Unit] = {
    Try(client.connect()) match {
      case Success(()) =>
        Futures.within(connectTimeout)(connectPromise.future)(ExecutionContexts.cached)
      case Failure(t) =>
        connectPromise tryFailure t
        connectPromise.future
    }
  }

  def onRawMessage(raw: String) = parse(raw).foreach(msg => {
    onMessage(msg)
    subject onNext msg
  })

  def onMessage(json: T) = ()

  /**
   * Might at least fail with a [[java.nio.channels.NotYetConnectedException]] or [[java.io.IOException]].
   *
   * @param json payload
   * @return
   */
  override def send(json: T): Try[Unit] = Try {
    client send stringify(json)
  }

  def isConnected = client.getConnection.isOpen

  def close(): Unit = client.close()

  override def onClose(): Unit = ()

  override def onError(t: Exception): Unit = ()
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy