Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial load

  • Loading branch information...
commit 8ed383a7cbba5ede15e604635813670a905ad19e 0 parents
Mahmood Ali authored
29 .gitignore
@@ -0,0 +1,29 @@
+# sbt files
+lib_managed
+bin
+project/boot
+project/build/target
+target
+
+**/lib_managed
+**/bin
+**/project/boot
+**/project/build/target
+**/target
+
+# Eclipse
+.project
+.classpath
+.metadata
+.scala_dependencies
+
+# random files
+*~
+.#*
+TAGS
+tags
+*.swp
+
+# random script files
+MyTest.scala
+
9 project/build.properties
@@ -0,0 +1,9 @@
+#Project properties
+#Fri May 07 14:07:10 EDT 2010
+project.organization=com.notnoop.smartpush
+project.name=notifier
+sbt.version=0.7.2
+project.version=1.0
+def.scala.version=2.7.7
+build.scala.versions=2.7.7
+project.initialize=false
13 project/build/NotifierProject.scala
@@ -0,0 +1,13 @@
+import sbt._
+
+class NotifierProject(info: ProjectInfo) extends DefaultProject(info) {
+
+ val notnoopRepo = "Notnoop Repo" at "http://notnoop.github.com/m2-repo"
+ val lagRepo = "Lag Repo" at "http://www.lag.net/repo/"
+
+ val javaapns = "com.notnoop.apns" % "apns" % "0.1.4"
+
+ val configgy = "net.lag" % "configgy" % "1.5"
+ val logback = "ch.qos.logback" % "logback-classic" % "0.9.17"
+}
+
9 src/main/resources/notifier.conf
@@ -0,0 +1,9 @@
+include "$(HOME)/.smartpush/smartpush.conf"
+include "$(HOME)/.smartpush/notifier.conf"
+
+amqp {
+ exchange ?= "smartpush"
+ queue ?= "smartpush.notification"
+ routing ?= "smartpush.notification"
+}
+
53 src/main/scala/AMQPConsumer.scala
@@ -0,0 +1,53 @@
+package com.notnoop.smartpush.notifier
+
+import org.slf4j.LoggerFactory
+
+import com.rabbitmq.client.{ConnectionFactory,ConnectionParameters,Channel}
+import com.rabbitmq.client.QueueingConsumer
+
+trait MQHandler {
+ def handleRequest(msg: Array[Byte]): Boolean
+}
+
+object MQChannel {
+ def apply(exchangeName: String, queueName: String, routingKey: String) = {
+ val durable = true
+
+ val params = new ConnectionParameters();
+
+ val conFactory = new ConnectionFactory(params);
+
+ val conn = conFactory.newConnection("localhost");
+ val channel = conn.createChannel();
+
+ channel.exchangeDeclare(exchangeName, "direct", durable)
+ channel.queueDeclare(queueName, durable)
+ channel.queueBind(queueName, exchangeName, routingKey)
+
+ channel
+ }
+}
+
+class NotificationListener(channel: Channel, handler :MQHandler,
+ queueName: String) {
+ val logger = LoggerFactory.getLogger(getClass)
+
+ def run() = {
+ val consumer = new QueueingConsumer(channel)
+ channel.basicConsume(queueName, false, consumer)
+
+ while (true) {
+ try {
+ val delivery = consumer.nextDelivery();
+ logger.debug("Received new request")
+ val handled = handler.handleRequest(delivery.getBody)
+ if (handled) {
+ channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
+ }
+ } catch {
+ case e => logger.error("Error while handling new message", e)
+ }
+ }
+ }
+}
+
62 src/main/scala/ApnsSender.scala
@@ -0,0 +1,62 @@
+package com.notnoop.smartpush.notifier
+
+import com.notnoop.apns.{APNS, ApnsService}
+import com.notnoop.apns.ReconnectPolicy.Provided.EVERY_HALF_HOUR
+
+import org.slf4j.LoggerFactory
+
+import java.math.BigInteger
+
+import scala.util.parsing.json._
+
+class ApnsSender(service: ApnsService) {
+ val logger = LoggerFactory.getLogger(getClass)
+
+ def this(keyStore: String, keyPass: String) = {
+ this(APNS.newService().withCert(keyStore, keyPass)
+ .withReconnectPolicy(EVERY_HALF_HOUR)
+ .withProductionDestination()
+ .build())
+ }
+
+ def toHex(id: String) = new BigInteger(id).toString(16).toLowerCase()
+ def urlOf(threadidHex: String) = "https://mail.google.com/mail/s/#cv/Inbox/" + threadidHex
+
+ def sendMessage(token: String, message: String, threadid: String, badge: Int) {
+ val threadidHex = toHex(threadid)
+ val url = urlOf(threadidHex)
+
+ val payload = APNS.newPayload().sound("default")
+ .alertBody(message)
+ .customField("threadid", threadidHex)
+ .customField("msg.url", url)
+ .badge(badge).shrinkBody()
+ .build();
+
+ logger.debug("Notifying {} for message {}", token, message)
+ service.push(token, payload)
+ }
+}
+
+class MQApnsHandler(sender: ApnsSender) extends MQHandler {
+ val logger = LoggerFactory.getLogger(getClass)
+
+ def handleRequest(msg: Array[Byte]) = {
+ try {
+ val json = JSON.parseFull(new String(msg, "UTF-8")).get.asInstanceOf[Map[String, Any]]
+
+ val token = json("token").asInstanceOf[String]
+ val message = json("message").asInstanceOf[String]
+ val threadid = json("threadid").asInstanceOf[String]
+ val badge = json.get("badge").getOrElse(0).asInstanceOf[Number].intValue
+
+ sender.sendMessage(token, message, threadid, badge)
+ true
+ } catch {
+ case e =>
+ logger.error("Unexpected error while handling message", e)
+ false
+ }
+ }
+}
+
36 src/main/scala/Boot.scala
@@ -0,0 +1,36 @@
+package com.notnoop.smartpush.notifier
+
+import org.slf4j.LoggerFactory
+import net.lag.configgy.Configgy
+
+object Boot {
+ val logger = LoggerFactory.getLogger(getClass)
+
+ def configure(filename: Option[String]) = {
+ filename match {
+ case Some(path) => Configgy.configure(path)
+ case None => Configgy.configureFromResource("notifier.conf")
+ }
+
+ Configgy.config
+ }
+
+ def main(args: Array[String]) {
+ val config = configure(args.firstOption)
+
+ val keyStore = config.getString("apns.keystore.path").get
+ val password = config.getString("apns.keystore.pass").get
+
+ val exchange = config.getString("amqp.exchange", "smartpush")
+ val queue = config.getString("amqp.queue", "smartpush.notification")
+ val routing = config.getString("amqp.routing", "smartpush.notification")
+
+ val handler = new MQApnsHandler(new ApnsSender(keyStore, password))
+
+ val listener = new NotificationListener(MQChannel(exchange, queue, routing), handler, queue)
+
+ logger.debug("starting")
+ listener.run()
+ }
+}
+
Please sign in to comment.
Something went wrong with that request. Please try again.