Статьи

Написание кометных приложений с использованием Scala и фреймворка Atmosphere

Написание приложений на основе кометы Атмосфера проста. Представьте себе использование Scala вместо Java … это становится действительно простым! Не нужно больше учиться лифту ?

Не будучи специалистом по Scala, я решил переписать приложение на основе чата, которое я недавно использовал при добавлении поддержки кластеров в Atmosphere. Это действительно мое первое Scala-приложение, поэтому не прыгайте, глядя на код! Вместо этого, твит мне лучшую реализацию. Я не буду вдаваться в подробности того, как написать приложение Atmosphere, поэтому, если вы впервые узнаете об Atmosphere, начните здесь .

Для чата мне нужно было реализовать два метода: один будет вызываться при приостановке ответа ( метод потоковой передачи HTTP Comet ), а другой — для отправки сообщений чата на этот приостановленный ответ (другому участнику чата). Чтобы сделать это в Scala, я сделал:

  package org.atmosphere.samples.scala.chat

import javax.ws.rs.{GET, POST, Path, Produces, WebApplicationException, Consumes}
import javax.ws.rs.core.MultivaluedMap
import org.atmosphere.core.annotation.{Broadcast, BroadcastFilter, Suspend}
import org.atmosphere.util.XSSHtmlFilter

@Path("/chat")
class Chat {

var JUNK : String = "<!-- Comet is a programming technique that enables web " +
"servers to send data to the client without having any need " +
"for the client to request it. -->\n"

@Suspend
@GET
@Produces(Array("text/html;charset=ISO-8859-1"))
def suspend() = {
var s = new StringBuilder()
for (i <- 0 to 10){
s.append(JUNK)
}
s.toString()
}

@Broadcast
@Consumes(Array("application/x-www-form-urlencoded"))
@POST
@Produces(Array("text/html"))
@BroadcastFilter(Array(classOf[XSSHtmlFilter],classOf[JsonpFilter]))
def publishMessage(form: MultivaluedMap[String, String]) = {
val action = form.getFirst("action")
val name = form.getFirst("name")

val result: String = if ("login".equals(action)) "System Message" + "__" + name + " has joined."
else if ("post".equals(action)) name + "__" + form.getFirst("message")
else throw new WebApplicationException(422)

result
}
}

Чтобы приостановить ответ, я аннотировал метод suspend () (строка 15) с помощью @Suspend. Возвращаемое значение метода suspend () будет записано, а затем ответ будет приостановлен, ожидая события сервера, например, сообщений из другого чата. Теперь, когда кто-то публикует сообщение, будет вызван метод publishMessage (). Поскольку метод аннотирован @Broadcast (строка 26), возвращаемое значение метода будет передано всем приостановленным ответам, например, все приостановленные соединения получат возможность написать сообщение. Но так как я не хочу, чтобы пользователи публиковали сценарии или любые виды атак, я также добавил аннотацию @BroadcastFilter , которая обязательно отфильтрует вредоносные символы. Так как мой клиент JavaScript ожидает ответа JSONp, я решил написать BroadcastFilterв Scala, которые преобразуют сообщение:

  package org.atmosphere.samples.scala.chat

import org.atmosphere.cpr.BroadcastFilter

class JsonpFilter extends BroadcastFilter[String] {

val BEGIN_SCRIPT_TAG = "<script type='text/javascript'>\n"
val END_SCRIPT_TAG = "</script>\n"

def filter(m : String) = {
var name = m
var message = ""

if (m.indexOf("__") > 0) {
name = m.substring(0, m.indexOf("__"))
message = m.substring(m.indexOf("__") + 2)
}

val result: String = (BEGIN_SCRIPT_TAG + "window.parent.app.update({ name: \""
+ name + "\", message: \""
+ message + "\" });\n"
+ END_SCRIPT_TAG)
}
}

Затем я просто передаю имя этого класса в аннотацию @BroadcastFilter:

 @BroadcastFilter(Array(classOf[XSSHtmlFilter],classOf[JsonpFilter]))

Что меня удивляет, так это то, что один фильтр написан на Java, а другой — на Scala !. Вот и все. С этими двумя классами работает демо-чат Atmosphere REST Chat. Чтобы усложнить приложение, я решил развернуть его в кластере. Этого легко достичь, пометив publishMesaage с помощью:

  @Cluster(Array(classOf[JGroupsFilter]))

Теперь мое Scala-приложение поддерживает Comet-Cluster! Это было весело!

По любым вопросам или для загрузки приведенного выше примера перейдите на наш основной сайт и воспользуйтесь нашим форумом Nabble (подписка не требуется) или подпишитесь на нас в Twitter и разместите там свои вопросы!