Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit 0a3b6eb5102e818085b6f1ce69abbc315ae0b2cc Patrick Burke committed Aug 27, 2012
@@ -0,0 +1,2 @@
+gen/
+target/
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="patburke.sse"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk android:minSdkVersion="10" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:name=".Application">
+ <activity android:name=".MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <service android:name=".ServerService" />
+ </application>
+
+</manifest>
@@ -0,0 +1,35 @@
+(defproject android-netty-sse/android-netty-sse "0.0.1-SNAPSHOT"
+ :description "FIXME: Android project description"
+ :url "http://example.com/FIXME"
+ :license {:name "Eclipse Public License"
+ :url "http://www.eclipse.org/legal/epl-v10.html"}
+ :min-lein-version "2.0.0"
+
+ :warn-on-reflection true
+
+ :source-paths ["src/clojure"]
+ :java-source-paths ["src/java" "gen"]
+ ;; The following two definitions are optional. The default
+ ;; target-path is "target", but you can change it to whatever you like.
+ ;; :target-path "bin"
+ ;; :compile-path "bin/classes"
+ :aot :all-with-unused ;; This one is necessary, please keep it
+ :aot-exclude-ns ["clojure.parallel"]
+
+ :dependencies [[android/clojure "1.4.0"]
+ [neko/neko "2.0.0-beta1"]
+ [io.netty/netty "3.5.4.Final"]]
+ :profiles {:dev {:dependencies [[android/tools.nrepl "0.2.0-bigstack"]]}
+ :release {:android
+ {;; Specify the path to your private
+ ;; keystore and the the alias of the
+ ;; key you want to sign APKs with.
+ ;; :keystore-path "/home/user/.android/private.keystore"
+ ;; :key-alias "mykeyalias"
+ }
+ :aot :all}}
+
+ :android {;; Specify the path to the Android SDK directory either
+ ;; here or in your ~/.lein/profiles.clj file.
+ ;; :sdk-path "/home/user/path/to/android-sdk/"
+ :target-version "10"})
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">Server Sent Events</string>
+
+</resources>
@@ -0,0 +1,63 @@
+;;; import_static.clj -- import static Java methods/fields into Clojure
+
+;; by Stuart Sierra, http://stuartsierra.com/
+;; June 1, 2008
+
+;; Copyright (c) Stuart Sierra, 2008. All rights reserved. The use
+;; and distribution terms for this software are covered by the Eclipse
+;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+;; which can be found in the file epl-v10.html at the root of this
+;; distribution. By using this software in any fashion, you are
+;; agreeing to be bound by the terms of this license. You must not
+;; remove this notice, or any other, from this software.
+
+
+
+(ns
+ #^{:author "Stuart Sierra",
+ :doc "Import static Java methods/fields into Clojure"}
+ clojure.contrib.import-static
+ (:use clojure.set))
+
+(defmacro import-static
+ "Imports the named static fields and/or static methods of the class
+ as (private) symbols in the current namespace.
+
+ Example:
+ user=> (import-static java.lang.Math PI sqrt)
+ nil
+ user=> PI
+ 3.141592653589793
+ user=> (sqrt 16)
+ 4.0
+
+ Note: The class name must be fully qualified, even if it has already
+ been imported. Static methods are defined as MACROS, not
+ first-class fns."
+ [class & fields-and-methods]
+ (let [only (set (map str fields-and-methods))
+ the-class (. Class forName (str class))
+ static? (fn [x]
+ (. java.lang.reflect.Modifier
+ (isStatic (. x (getModifiers)))))
+ statics (fn [array]
+ (set (map (memfn getName)
+ (filter static? array))))
+ all-fields (statics (. the-class (getFields)))
+ all-methods (statics (. the-class (getMethods)))
+ fields-to-do (intersection all-fields only)
+ methods-to-do (intersection all-methods only)
+ make-sym (fn [string]
+ (with-meta (symbol string) {:private true}))
+ import-field (fn [name]
+ (list 'def (make-sym name)
+ (list '. class (symbol name))))
+ import-method (fn [name]
+ (list 'defmacro (make-sym name)
+ '[& args]
+ (list 'list ''. (list 'quote class)
+ (list 'apply 'list
+ (list 'quote (symbol name))
+ 'args))))]
+ `(do ~@(map import-field fields-to-do)
+ ~@(map import-method methods-to-do))))
@@ -0,0 +1,37 @@
+(ns patburke.sse.main
+ (:use [neko.activity :only [defactivity set-content-view!]]
+ [neko.threading :only [on-ui]]
+ [neko.ui :only [make-ui]]
+ [neko.application :only [defapplication]]
+ [neko.notify :only[toast]])
+ (:require [patburke.sse.server :as s])
+ (:import [android.app Service]))
+
+(defapplication patburke.sse.Application)
+
+;; SSE Server Service
+;(gen-class patburke.sse.ServerService
+ ;:extends [Service]
+
+(declare ^android.widget.EditText message-input)
+
+(defactivity patburke.sse.MainActivity
+ :def a
+ :on-create
+ (fn [this bundle]
+ (.start (Thread. (fn [] (s/run))))
+ (on-ui
+ (set-content-view! a
+ (make-ui [:linear-layout {:layout-width :fill
+ :layout-height :fill
+ :orientation :vertical}
+ [:edit-text {:def message-input
+ :layout-width :fill
+ :layout-height :wrap
+ :hint "Message"}]
+ [:button {:layout-width :fill
+ :layout-height :wrap
+ :text "Send"
+ :on-click
+ (fn [_]
+ (s/send-message (.getText message-input)))}]])))))
@@ -0,0 +1,82 @@
+(ns patburke.sse.server
+ (:use [clojure.contrib.import-static :only [import-static]])
+ (:import
+ [java.net InetSocketAddress]
+ [java.util.concurrent Executors]
+ [org.jboss.netty.bootstrap ServerBootstrap]
+ [org.jboss.netty.buffer ChannelBuffers]
+ [org.jboss.netty.channel Channels ChannelPipelineFactory
+ SimpleChannelHandler SimpleChannelUpstreamHandler ChannelFutureListener]
+ [org.jboss.netty.channel.socket.nio NioServerSocketChannelFactory]
+ [org.jboss.netty.handler.codec.http HttpRequestDecoder HttpResponseEncoder
+ HttpChunkAggregator HttpContentCompressor DefaultHttpResponse
+ HttpMethod]
+ [org.jboss.netty.channel.group DefaultChannelGroup]
+ [org.jboss.netty.util CharsetUtil]))
+
+(import-static org.jboss.netty.handler.codec.http.HttpVersion HTTP_1_1)
+(import-static org.jboss.netty.handler.codec.http.HttpResponseStatus
+ METHOD_NOT_ALLOWED OK)
+(import-static org.jboss.netty.handler.codec.http.HttpMethod GET)
+(import-static org.jboss.netty.handler.codec.http.HttpHeaders$Names CONTENT_TYPE)
+
+(defn- make-handler
+ "Takes a function of two arguments and returns an Upstream Handler."
+ [handler-fn]
+ (proxy [SimpleChannelUpstreamHandler] []
+ (messageReceived [ctx e]
+ (let [ch (.getChannel e)
+ req (.getMessage e)]
+ (handler-fn ch req)))
+
+ (exceptionCaught [ctx e]
+ (.. e (getCause) (printStackTrace))
+ (.. e (getChannel) (close)))))
+
+(defn- start-server
+ "Start the netty server."
+ [handler-fn port]
+ (let [handler (make-handler handler-fn)
+ bootstrap (ServerBootstrap.
+ (NioServerSocketChannelFactory.
+ (Executors/newCachedThreadPool)
+ (Executors/newCachedThreadPool)))]
+ (.setPipelineFactory bootstrap
+ (reify ChannelPipelineFactory
+ (getPipeline [this]
+ (let [p (Channels/pipeline)]
+ (doto p
+ (.addLast "decoder" (HttpRequestDecoder.))
+ (.addLast "aggregator" (HttpChunkAggregator. 1048576))
+ (.addLast "encoder" (HttpResponseEncoder.))
+ (.addLast "deflater" (HttpContentCompressor.))
+ (.addLast "handler" handler))
+ p))))
+ (.bind bootstrap (InetSocketAddress. port))))
+
+(def message-group (DefaultChannelGroup.))
+
+(defn send-message
+ [message]
+ (.write message-group
+ (ChannelBuffers/copiedBuffer
+ (str "data: " message "\n\n") (CharsetUtil/UTF-8))))
+
+(defn- sse-handler
+ [ch req]
+ (if-not (= (.getMethod req) GET)
+ (let [not-allowed (DefaultHttpResponse. HTTP_1_1 METHOD_NOT_ALLOWED)]
+ (.write ch not-allowed)
+ (.close ch))
+ (let [sse (DefaultHttpResponse. HTTP_1_1 OK)]
+ (.setHeader sse CONTENT_TYPE "text/event-stream")
+ (.addListener (.write ch sse)
+ (reify ChannelFutureListener
+ (operationComplete [this cf]
+ (if (.isSuccess cf)
+ (.add message-group ch)
+ (.close ch))))))))
+
+(defn run
+ []
+ (start-server sse-handler 8080))

0 comments on commit 0a3b6eb

Please sign in to comment.