diff --git a/simple-demo/simple-demo-rest/.classpath b/simple-demo/simple-demo-rest/.classpath
new file mode 100644
index 0000000..bc5aeac
--- /dev/null
+++ b/simple-demo/simple-demo-rest/.classpath
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/simple-demo/simple-demo-rest/.gitignore b/simple-demo/simple-demo-rest/.gitignore
new file mode 100644
index 0000000..cc17820
--- /dev/null
+++ b/simple-demo/simple-demo-rest/.gitignore
@@ -0,0 +1,41 @@
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+target
+report
+build
+.settings
+log
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+# =========================
+# Operating System Files
+# =========================
+
+# OSX
+# =========================
+
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must ends with two \r.
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
diff --git a/simple-demo/simple-demo-rest/.project b/simple-demo/simple-demo-rest/.project
new file mode 100644
index 0000000..501e5e8
--- /dev/null
+++ b/simple-demo/simple-demo-rest/.project
@@ -0,0 +1,35 @@
+
+
+ simple-demo-rest
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ javafx.eclipse.f3editor.builder.F3Builder
+
+
+
+
+ org.springframework.ide.eclipse.core.springbuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.springframework.ide.eclipse.core.springnature
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.jdt.core.javanature
+ javafx.eclipse.f3editor.builder.F3Nature
+
+
diff --git a/simple-demo/simple-demo-rest/bin/start.bat b/simple-demo/simple-demo-rest/bin/start.bat
new file mode 100644
index 0000000..95f0b88
--- /dev/null
+++ b/simple-demo/simple-demo-rest/bin/start.bat
@@ -0,0 +1,14 @@
+@echo off
+echo Starting application. Please wait...
+
+cd ..
+
+echo %CLASSPATH%
+
+set CLASSPATH=etc/.;lib/*;~1%;.
+
+echo %CLASSPATH%
+
+rem java "%JAVA_OPTS%" -classpath %CLASSPATH% org.simpleframework.demo.ApplicationLauncher etc/spring.xml etc/common.properties etc/local.properties
+java "%JAVA_OPTS%" -classpath %CLASSPATH% org.simpleframework.demo.ApplicationLauncher etc/spring.xml etc/common.properties etc/local.properties
+
diff --git a/simple-demo/simple-demo-rest/data/chat/index.html b/simple-demo/simple-demo-rest/data/chat/index.html
new file mode 100644
index 0000000..4cac24a
--- /dev/null
+++ b/simple-demo/simple-demo-rest/data/chat/index.html
@@ -0,0 +1,12 @@
+
+
+ Login Page
+
+
+ Please Login
+
+
+
\ No newline at end of file
diff --git a/simple-demo/simple-demo-rest/data/chat/login.html b/simple-demo/simple-demo-rest/data/chat/login.html
new file mode 100644
index 0000000..50f3cbd
--- /dev/null
+++ b/simple-demo/simple-demo-rest/data/chat/login.html
@@ -0,0 +1,35 @@
+
+
+ Chat Room
+
+
+
+ Chat Room
+ Refresh browser to clear page and resubscribe
+
+
+
+
+
+
\ No newline at end of file
diff --git a/simple-demo/simple-demo-rest/etc/common.properties b/simple-demo/simple-demo-rest/etc/common.properties
new file mode 100644
index 0000000..eb4bbfa
--- /dev/null
+++ b/simple-demo/simple-demo-rest/etc/common.properties
@@ -0,0 +1,3 @@
+log4j.configFile=etc/log4j.xml
+
+server.listenPort=6060
diff --git a/simple-demo/simple-demo-rest/etc/local.properties b/simple-demo/simple-demo-rest/etc/local.properties
new file mode 100644
index 0000000..0aa4ef7
--- /dev/null
+++ b/simple-demo/simple-demo-rest/etc/local.properties
@@ -0,0 +1 @@
+context.path=chat
\ No newline at end of file
diff --git a/simple-demo/simple-demo-rest/etc/log4j.xml b/simple-demo/simple-demo-rest/etc/log4j.xml
new file mode 100644
index 0000000..979f1cc
--- /dev/null
+++ b/simple-demo/simple-demo-rest/etc/log4j.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/simple-demo/simple-demo-rest/etc/spring.xml b/simple-demo/simple-demo-rest/etc/spring.xml
new file mode 100644
index 0000000..d53750c
--- /dev/null
+++ b/simple-demo/simple-demo-rest/etc/spring.xml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/simple-demo/simple-demo-rest/pom.xml b/simple-demo/simple-demo-rest/pom.xml
new file mode 100644
index 0000000..0247456
--- /dev/null
+++ b/simple-demo/simple-demo-rest/pom.xml
@@ -0,0 +1,137 @@
+
+ 4.0.0
+ simple-demo-rest
+ org.simpleframework
+ 6.0.1
+ jar
+
+
+ 1.6
+ 1.6
+ ISO-8859-1
+ 1.82
+
+
+
+
+ junit
+ junit
+ 3.8.1
+ test
+
+
+ org.simpleframework
+ simple-http
+ 6.0.1
+
+
+ org.simpleframework
+ simple-common
+ 6.0.1
+
+
+ org.simpleframework
+ simple-transport
+ 6.0.1
+
+
+ org.simpleframework
+ simple-demo
+ 6.0.1
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.2.3
+
+
+ com.google.code.gson
+ gson
+ 2.3.1
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+
+ local
+
+
+
+ maven-antrun-plugin
+
+
+ local
+ package
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ maven-assembly-plugin
+
+ websocket-chat-${version}
+
+
+
+ local
+ package
+
+ single
+
+
+
+ ${basedir}/src/main/assembly/filter-local.properties
+
+
+ ${project.build.directory}/assembly/assembly-local.xml
+
+
+
+
+
+
+
+
+
+
diff --git a/simple-demo/simple-demo-rest/src/main/assembly/assembly.xml b/simple-demo/simple-demo-rest/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..85088be
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/assembly/assembly.xml
@@ -0,0 +1,78 @@
+
+ @archive@
+
+ tar
+
+
+
+ /lib
+ ${artifact.artifactId}.${artifact.extension}
+
+
+
+
+ ${project.basedir}/bin
+ bin
+ 0755
+ 0755
+ unix
+ true
+
+ *.sh
+ *.bat
+
+
+
+ ${project.basedir}/etc
+ etc
+ 0644
+ 0755
+ true
+
+ @env@.properties
+ common.properties
+ jmxremote.password
+ log4j.xml
+ spring.xml
+ spring-*.xml
+
+
+
+ ${project.basedir}/etc
+ etc
+ 0644
+ 0755
+ false
+
+ @env@.yieldbroker.com.pfx
+
+
+
+ ${project.basedir}/error
+ error
+ 0644
+ 0755
+ false
+
+ **/*.html
+
+
+
+ ${project.basedir}/template
+ template
+ 0644
+ 0755
+ false
+
+ **/*.png
+ **/*.gif
+ **/*.jpg
+ **/*.vm
+ **/*.html
+ **/*.css
+ **/*.js
+
+
+
+
diff --git a/simple-demo/simple-demo-rest/src/main/assembly/filter-local.properties b/simple-demo/simple-demo-rest/src/main/assembly/filter-local.properties
new file mode 100644
index 0000000..9be7e73
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/assembly/filter-local.properties
@@ -0,0 +1,3 @@
+env=local
+
+jvm.memory=-Xrunjdwp:transport=dt_socket,server=y,address=8942,suspend=n -Xms512m -Xmx512m -XX:MaxPermSize=128m -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -XX:+CMSPermGenSweepingEnabled
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRequest.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRequest.java
new file mode 100644
index 0000000..2ec12a5
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRequest.java
@@ -0,0 +1,26 @@
+package org.simpleframework.demo.rest;
+
+public class ChatRequest {
+
+ private final String message;
+ private final String user;
+ private final String time;
+
+ public ChatRequest(String message, String user, String time) {
+ this.message = message;
+ this.user = user;
+ this.time = time;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public String getTime() {
+ return time;
+ }
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRequestDistributor.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRequestDistributor.java
new file mode 100644
index 0000000..3b7a762
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRequestDistributor.java
@@ -0,0 +1,44 @@
+package org.simpleframework.demo.rest;
+
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.FrameListener;
+import org.simpleframework.http.socket.Reason;
+import org.simpleframework.http.socket.Session;
+
+import com.google.gson.Gson;
+
+public class ChatRequestDistributor implements FrameListener {
+
+ private final MessagePublisher publisher;
+ private final Gson gson;
+
+ public ChatRequestDistributor(MessagePublisher publisher) {
+ this.gson = new Gson();
+ this.publisher = publisher;
+ }
+
+
+ @Override
+ public void onFrame(Session session, Frame frame) {
+ String text = frame.getText();
+ Object value = gson.fromJson(text, ChatRequest.class);
+
+ try {
+ publisher.publish(value);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onError(Session session, Exception cause) {
+ cause.printStackTrace();
+ }
+
+ @Override
+ public void onClose(Session session, Reason reason) {
+ String message = reason.getText();
+ System.err.println(message);
+ }
+
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRequestHandler.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRequestHandler.java
new file mode 100644
index 0000000..3fc188a
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRequestHandler.java
@@ -0,0 +1,26 @@
+package org.simpleframework.demo.rest;
+
+import org.simpleframework.http.socket.FrameChannel;
+import org.simpleframework.http.socket.Session;
+import org.simpleframework.http.socket.service.Service;
+
+public class ChatRequestHandler implements Service { // forwards on the chat messages from the WebSocket!!
+
+ private final ChatRequestDistributor distributor;
+
+ public ChatRequestHandler(MessagePublisher publisher) {
+ this.distributor = new ChatRequestDistributor(publisher);
+ }
+
+ @Override
+ public void connect(Session connection) {
+ FrameChannel socket = connection.getChannel();
+
+ try {
+ socket.register(distributor);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRoom.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRoom.java
new file mode 100644
index 0000000..236b29e
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRoom.java
@@ -0,0 +1,79 @@
+package org.simpleframework.demo.rest;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.log4j.Logger;
+import org.simpleframework.http.Cookie;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.Session;
+import org.simpleframework.http.socket.FrameChannel;
+import org.simpleframework.http.socket.service.Service;
+
+public class ChatRoom implements Service {
+
+ private static final Logger LOG = Logger.getLogger(ChatRoom.class);
+
+ private final ChatRoomListener listener;
+ private final Map sockets;
+ private final Set users;
+
+ public ChatRoom() {
+ this.listener = new ChatRoomListener(this);
+ this.sockets = new ConcurrentHashMap();
+ this.users = new CopyOnWriteArraySet();
+ }
+
+ public void connect(Session connection) {
+ FrameChannel socket = connection.getChannel();
+ Request req = connection.getRequest();
+ Cookie user = req.getCookie("user");
+
+ if(user == null) {
+ user = new Cookie("user", "anonymous");
+ }
+ String name = user.getValue();
+
+ try {
+ socket.register(listener);
+ join(name, socket);
+ } catch(Exception e) {
+ LOG.info("Problem joining chat room", e);
+ }
+
+ }
+
+ public void join(String user, FrameChannel operation) {
+ sockets.put(user, operation);
+ users.add(user);
+ }
+
+ public void leave(String user, FrameChannel operation){
+ sockets.put(user, operation);
+ users.add(user);
+ }
+
+ public void distribute(String from, Frame frame) {
+ try {
+ for(String user : users) {
+ FrameChannel operation = sockets.get(user);
+
+ try {
+ if(!from.equals(user)) {
+ operation.send(frame);
+ }
+ } catch(Exception e){
+ sockets.remove(user);
+ users.remove(user);
+ operation.close();
+ LOG.info("Problem sending message", e);
+ }
+ }
+ } catch(Exception e) {
+ LOG.info("Problem distributing message", e);
+ }
+ }
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRoomListener.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRoomListener.java
new file mode 100644
index 0000000..c6cb25b
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRoomListener.java
@@ -0,0 +1,45 @@
+package org.simpleframework.demo.rest;
+
+import org.apache.log4j.Logger;
+import org.simpleframework.http.Cookie;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.socket.DataFrame;
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.FrameListener;
+import org.simpleframework.http.socket.FrameType;
+import org.simpleframework.http.socket.Reason;
+import org.simpleframework.http.socket.Session;
+
+public class ChatRoomListener implements FrameListener {
+
+ private static final Logger LOG = Logger.getLogger(ChatRoomListener.class);
+
+ private final ChatRoom room;
+
+ public ChatRoomListener(ChatRoom room) {
+ this.room = room;
+ }
+
+ public void onFrame(Session socket, Frame frame) {
+ FrameType type = frame.getType();
+ String text = frame.getText();
+ Request request = socket.getRequest();
+ Cookie user = request.getCookie("user");
+ String name = user.getValue();
+
+ if(type == FrameType.TEXT){
+ Frame replay = new DataFrame(type, "(" + name + ") " +text);
+
+ room.distribute(name, replay);
+ }
+ LOG.info("onFrame(" + type + ")");
+ }
+
+ public void onError(Session socket, Exception cause) {
+ LOG.info("onError(" + cause + ")", cause);
+ }
+
+ public void onClose(Session session, Reason reason) {
+ LOG.info("onClose(" + reason + ")");
+ }
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRoomLogin.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRoomLogin.java
new file mode 100644
index 0000000..7977e59
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatRoomLogin.java
@@ -0,0 +1,23 @@
+package org.simpleframework.demo.rest;
+
+import org.simpleframework.demo.http.resource.Resource;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+
+public class ChatRoomLogin implements Resource {
+
+ private final Resource resource;
+
+ public ChatRoomLogin(Resource resource) {
+ this.resource = resource;
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Throwable {
+ String name = request.getParameter("user");
+
+ response.setCookie("user", name);
+ resource.handle(request, response);
+ }
+
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatServer.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatServer.java
new file mode 100644
index 0000000..00f3853
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ChatServer.java
@@ -0,0 +1,13 @@
+package org.simpleframework.demo.rest;
+
+public class ChatServer {
+
+ public static final int PORT = 8991;
+
+ public static void main(String[] list) throws Exception {
+ MessageServer server = new MessageServer();
+ MessagePublisher publisher = server.create(PORT);
+ ChatRequestDistributor distributor = new ChatRequestDistributor(publisher);
+
+ }
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/LeaseSubscriber.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/LeaseSubscriber.java
new file mode 100644
index 0000000..21043da
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/LeaseSubscriber.java
@@ -0,0 +1,68 @@
+package org.simpleframework.demo.rest;
+
+import java.util.concurrent.ThreadFactory;
+
+import org.simpleframework.common.thread.DaemonFactory;
+import org.simpleframework.http.Method;
+
+import com.google.gson.Gson;
+
+public class LeaseSubscriber {
+
+ private final ThreadFactory factory;
+ private final Gson gson;
+ private final String remote;
+ private final String local;
+ private final String key;
+
+ public LeaseSubscriber(String key, String remote, String local) {
+ this.factory = new DaemonFactory(SubscriptionPublisher.class);
+ this.gson = new Gson();
+ this.key = key;
+ this.local = local;
+ this.remote = remote;
+ }
+
+ public void subscribe(String address, String filter) throws Exception {
+ SubscribeRequest request = new SubscribeRequest(key, local, filter, 10000);
+ RequestBuilder builder = new RequestBuilder(remote);
+ SubscriptionPublisher publisher = new SubscriptionPublisher(remote);
+ String value = gson.toJson(request);
+ Thread thread = factory.newThread(publisher);
+
+ builder.setPath("/" + MessageServer.SUBSCRIBE_PREFIX);
+ builder.setMethod(Method.POST);
+ builder.setBody(value);
+ builder.execute(String.class);
+ thread.start();
+ }
+
+ private class SubscriptionPublisher implements Runnable {
+
+ private final String remote;
+
+ public SubscriptionPublisher(String remote) {
+ this.remote = remote;
+ }
+
+ @Override
+ public void run() {
+ while(true) {
+ try {
+ RenewRequest request = new RenewRequest(key, 10000);
+ RequestBuilder builder = new RequestBuilder(remote);
+ String value = gson.toJson(request);
+
+ builder.setPath("/" + MessageServer.RENEW_PREFIX);
+ builder.setMethod(Method.POST);
+ builder.setBody(value);
+ builder.execute(String.class);
+ Thread.sleep(5000);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ }
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/LeaseSubscriptionManager.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/LeaseSubscriptionManager.java
new file mode 100644
index 0000000..9e9366c
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/LeaseSubscriptionManager.java
@@ -0,0 +1,101 @@
+package org.simpleframework.demo.rest;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+import org.simpleframework.common.lease.Cleaner;
+import org.simpleframework.common.lease.Lease;
+import org.simpleframework.common.lease.LeaseManager;
+
+public class LeaseSubscriptionManager implements SubscriptionManager {
+
+ private final Map subscriptions;
+ private final LeaseManager manager;
+ private final SubscriptionCleaner cleaner;
+
+ public LeaseSubscriptionManager() {
+ this.subscriptions = new ConcurrentHashMap();
+ this.cleaner = new SubscriptionCleaner();
+ this.manager = new LeaseManager(cleaner);
+ }
+
+ public List match(MatchRequest request) {
+ List addresses = new ArrayList();
+
+ if(!subscriptions.isEmpty()) {
+ Set> entries = subscriptions.entrySet();
+ Class type = request.getType();
+ String name = type.getName();
+
+ for(Entry entry : entries) {
+ Subscription subscription = entry.getValue();
+ String filter = subscription.getFilter();
+ String address = subscription.getAddress();
+
+ if(filter.matches(name)) {
+ addresses.add(address);
+ }
+ }
+ }
+ return addresses;
+ }
+
+ public void subscribe(SubscribeRequest request) {
+ String key = request.getKey();
+ String address = request.getAddress();
+ String filter = request.getFilter();
+ long duration = request.getDuration();
+ Lease lease = manager.lease(key, duration, TimeUnit.MILLISECONDS);
+ Subscription subscription = new Subscription(lease, address, filter);
+ subscriptions.put(key, subscription);
+ }
+
+ public void renew(RenewRequest request) {
+ String key = request.getKey();
+ long duration = request.getDuration();
+ Subscription subscription = subscriptions.get(key);
+
+ if(subscription != null) {
+ Lease lease = subscription.getLease();
+ lease.renew(duration, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private class SubscriptionCleaner implements Cleaner {
+
+ public void clean(String key) {
+ subscriptions.remove(key);
+ }
+ }
+
+ private class Subscription {
+
+ private final Lease lease;
+ private final String address;
+ private final String filter;
+
+ public Subscription(Lease lease, String address, String filter) {
+ this.lease = lease;
+ this.address = address;
+ this.filter = filter;
+ }
+
+ public Lease getLease() {
+ return lease;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public String getFilter() {
+ return filter;
+ }
+
+ }
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/MatchRequest.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/MatchRequest.java
new file mode 100644
index 0000000..c212c61
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/MatchRequest.java
@@ -0,0 +1,14 @@
+package org.simpleframework.demo.rest;
+
+public class MatchRequest {
+
+ private final Class type;
+
+ public MatchRequest(Class type) {
+ this.type = type;
+ }
+
+ public Class getType() {
+ return type;
+ }
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/MessageListener.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/MessageListener.java
new file mode 100644
index 0000000..35545ef
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/MessageListener.java
@@ -0,0 +1,5 @@
+package org.simpleframework.demo.rest;
+
+public interface MessageListener {
+ void onMessage(T message) throws Exception;
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/MessagePublisher.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/MessagePublisher.java
new file mode 100644
index 0000000..bae0337
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/MessagePublisher.java
@@ -0,0 +1,5 @@
+package org.simpleframework.demo.rest;
+
+public interface MessagePublisher {
+ void publish(Object value) throws Exception;
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/MessageServer.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/MessageServer.java
new file mode 100644
index 0000000..2e9e7ea
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/MessageServer.java
@@ -0,0 +1,36 @@
+package org.simpleframework.demo.rest;
+
+import java.util.Collections;
+import java.util.List;
+
+public class MessageServer {
+
+ public static final String SUBSCRIBE_PREFIX = "subscribe";
+ public static final String RENEW_PREFIX = "renew";
+
+ private final List registrations;
+
+ public MessageServer() {
+ this(Collections.EMPTY_LIST);
+ }
+
+ public MessageServer(List registrations) {
+ this.registrations = registrations;
+ }
+
+ public MessagePublisher create(int port) throws Exception {
+ SubscriptionManager manager = new LeaseSubscriptionManager();
+ SubscribeRequestHandler subscriber = new SubscribeRequestHandler(manager);
+ RenewRequestHandler renewer = new RenewRequestHandler(manager);
+ RequestProcessor processor = new RequestProcessor(port);
+
+ processor.register(new RequestRegistration(SUBSCRIBE_PREFIX, subscriber, SubscribeRequest.class));
+ processor.register(new RequestRegistration(RENEW_PREFIX, renewer, RenewRequest.class));
+
+ for(RequestRegistration registration : registrations) {
+ processor.register(registration);
+ }
+ processor.start();
+ return new SubscriptionMessagePublisher(manager);
+ }
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RenewRequest.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RenewRequest.java
new file mode 100644
index 0000000..351de7e
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RenewRequest.java
@@ -0,0 +1,20 @@
+package org.simpleframework.demo.rest;
+
+public class RenewRequest {
+
+ private final String key;
+ private final long duration;
+
+ public RenewRequest(String key, long duration){
+ this.duration = duration;
+ this.key = key;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public long getDuration() {
+ return duration;
+ }
+}
\ No newline at end of file
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RenewRequestHandler.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RenewRequestHandler.java
new file mode 100644
index 0000000..d83a702
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RenewRequestHandler.java
@@ -0,0 +1,38 @@
+package org.simpleframework.demo.rest;
+
+import java.io.OutputStream;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+
+import com.google.gson.Gson;
+
+public class RenewRequestHandler implements RequestHandler {
+
+ private final SubscriptionManager manager;
+ private final Gson gson;
+
+ public RenewRequestHandler(SubscriptionManager manager) {
+ this.gson = new Gson();
+ this.manager = manager;
+ }
+
+ @Override
+ public void handle(Request request, Response response, RenewRequest message) throws Exception {
+ String key = message.getKey();
+ StatusResponse status = new StatusResponse(key, true);
+ manager.renew(message);
+ String content = gson.toJson(status);
+ OutputStream output = response.getOutputStream();
+ byte[] data = content.getBytes("UTF-8");
+
+ response.setContentType("text/json");
+ response.setContentLength(data.length);
+ output.write(data);
+ output.flush();
+ response.close();
+
+
+ }
+
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestBuilder.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestBuilder.java
new file mode 100644
index 0000000..972a6d2
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestBuilder.java
@@ -0,0 +1,409 @@
+package org.simpleframework.demo.rest;
+
+import static org.apache.http.conn.params.ConnRoutePNames.DEFAULT_PROXY;
+import static org.apache.http.params.CoreConnectionPNames.CONNECTION_TIMEOUT;
+import static org.apache.http.params.CoreConnectionPNames.SO_TIMEOUT;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.NoConnectionReuseStrategy;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.BasicClientConnectionManager;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.EntityUtils;
+
+public class RequestBuilder {
+
+ private final AtomicReference override;
+ private final AtomicReference body;
+ private final Map parameters;
+ private final Map headers;
+ private final Map cookies;
+ private final ClientConnectionManager manager;
+ private final ConnectionReuseStrategy strategy;
+ private final ResponseConverter converter;
+ private final AtomicBoolean post;
+ private final DefaultHttpClient client;
+ private final String address;
+ private final URI template;
+
+ public RequestBuilder(String address) throws Exception {
+ this.parameters = new HashMap();
+ this.headers = new HashMap();
+ this.cookies = new HashMap();
+ this.override = new AtomicReference();
+ this.body = new AtomicReference();
+ this.manager = new BasicClientConnectionManager();
+ this.strategy = new NoConnectionReuseStrategy();
+ this.converter = new ResponseConverter();
+ this.client = new DefaultHttpClient(manager);
+ this.template = new URI(address);
+ this.post = new AtomicBoolean();
+ this.address = address;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public RequestBuilder setBody(String value) {
+ if (body != null) {
+ body.set(value);
+ }
+ return this;
+ }
+
+ public RequestBuilder setPath(String path) {
+ if (path != null) {
+ override.set(path);
+ }
+ return this;
+ }
+
+ public RequestBuilder addCookie(String name, String value) {
+ if (value != null) {
+ cookies.put(name, value);
+ }
+ return this;
+ }
+
+ public RequestBuilder addHeader(String name, String value) {
+ if (value != null) {
+ headers.put(name, value);
+ }
+ return this;
+ }
+
+ public RequestBuilder addParameter(String name, String value) {
+ if (value != null) {
+ parameters.put(name, value);
+ }
+ return this;
+ }
+
+ public RequestBuilder setMethod(String method) {
+ if (method.equals(HttpPost.METHOD_NAME)) {
+ post.set(true);
+ return this;
+ }
+ if (method.equals(HttpGet.METHOD_NAME)) {
+ post.set(false);
+ return this;
+ }
+ throw new IllegalStateException("Method '" + method + "' is not supported");
+ }
+
+ public RequestBuilder setAuthorization(String name, String password) {
+ Credentials credentials = createCredentials(name, password);
+ CredentialsProvider provider = client.getCredentialsProvider();
+
+ if (provider != null) {
+ provider.setCredentials(AuthScope.ANY, credentials);
+ }
+ return this;
+ }
+
+ public RequestBuilder setConnectTimeout(int duration) throws Exception {
+ HttpParams parameters = client.getParams();
+
+ parameters.setParameter(CONNECTION_TIMEOUT, duration);
+
+ return this;
+ }
+
+ public RequestBuilder setReadTimeout(int duration) throws Exception {
+ HttpParams parameters = client.getParams();
+
+ parameters.setParameter(SO_TIMEOUT, duration);
+
+ return this;
+ }
+
+ public RequestBuilder setProxy(String address) throws Exception {
+ HttpHost proxy = createHost(address);
+ HttpParams parameters = client.getParams();
+
+ parameters.setParameter(DEFAULT_PROXY, proxy);
+
+ return this;
+ }
+
+ public T execute(Class type) throws Exception {
+ try {
+ if (post.get()) {
+ return executePost(type);
+ }
+ return executeGet(type);
+ } finally {
+ manager.shutdown();
+ }
+ }
+
+ private T executeGet(Class type) throws Exception {
+ HttpResponse response = executeGet();
+
+ if (converter.accept(type)) {
+ return convertResponse(response, type);
+ }
+ return createResponse(response, type);
+ }
+
+ private T executePost(Class type) throws Exception {
+ HttpResponse response = executePost();
+
+ if (converter.accept(type)) {
+ return convertResponse(response, type);
+ }
+ return createResponse(response, type);
+ }
+
+ private T createResponse(HttpResponse response, Class type) throws Exception {
+ HttpEntity entity = response.getEntity();
+
+ if (type == HttpResponse.class) {
+ return (T) response;
+ }
+ if (type == byte[].class) {
+ return (T) EntityUtils.toByteArray(entity);
+ }
+ if (type == InputStream.class) {
+ return (T) entity.getContent();
+ }
+ throw new IllegalStateException("Cannot convert response to " + type);
+ }
+
+ private T convertResponse(HttpResponse response, Class type) throws Exception {
+ HttpEntity entity = response.getEntity();
+ String body = EntityUtils.toString(entity);
+
+ if (body != null) {
+ return (T) converter.convert(type, body);
+ }
+ return null;
+ }
+
+ private HttpResponse executePost() throws Exception {
+ HttpPost message = createPost();
+ HttpEntity entity = createEntity();
+ URI target = createURI(message);
+
+ client.setReuseStrategy(strategy);
+ message.setURI(target);
+ message.setEntity(entity);
+
+ return client.execute(message);
+ }
+
+ private HttpResponse executeGet() throws Exception {
+ HttpGet message = createGet();
+ URI target = createURI(message);
+
+ client.setReuseStrategy(strategy);
+ message.setURI(target);
+
+ return client.execute(message);
+ }
+
+ private HttpGet createGet() throws Exception {
+ HttpGet get = new HttpGet(address);
+
+ if (!headers.isEmpty()) {
+ Set names = headers.keySet();
+
+ for (String name : names) {
+ String value = headers.get(name);
+ get.addHeader(name, value);
+ }
+ }
+ if (!cookies.isEmpty()) {
+ Set names = cookies.keySet();
+
+ for (String name : names) {
+ String value = cookies.get(name);
+ String cookie = String.format("%s=%s", name, value);
+
+ get.addHeader("Cookie", cookie);
+ }
+ }
+ return get;
+ }
+
+ private HttpPost createPost() throws Exception {
+ HttpPost post = new HttpPost(address);
+
+ if (!headers.isEmpty()) {
+ Set names = headers.keySet();
+
+ for (String name : names) {
+ String value = headers.get(name);
+ post.addHeader(name, value);
+ }
+ }
+ if (!cookies.isEmpty()) {
+ Set names = cookies.keySet();
+
+ for (String name : names) {
+ String value = cookies.get(name);
+ String cookie = String.format("%s=%s", name, value);
+
+ post.addHeader("Cookie", cookie);
+ }
+ }
+ return post;
+ }
+
+ private HttpHost createHost(String address) throws Exception {
+ URI target = URI.create(address);
+ String scheme = target.getScheme();
+ String host = target.getHost();
+ int port = target.getPort();
+
+ return new HttpHost(host, port, scheme);
+ }
+
+ private URI createURI(HttpPost message) throws Exception {
+ URI target = message.getURI();
+ String query = target.getQuery();
+
+ return createURI(message, query);
+ }
+
+ private URI createURI(HttpGet message) throws Exception {
+ String query = createQuery(message);
+
+ return createURI(message, query);
+ }
+
+ private URI createURI(HttpUriRequest message, String query) throws Exception {
+ URI target = message.getURI();
+ String fragment = target.getFragment();
+ String info = target.getUserInfo();
+ String path = extractPath(target);
+ String scheme = extractScheme(target);
+ String host = extractHost(target);
+ int port = extractPort(target);
+
+ return new URI(scheme, info, host, port, path, query, fragment);
+ }
+
+ private String createQuery(HttpGet message) throws Exception {
+ List parameters = createParameters();
+ URI target = message.getURI();
+ String query = target.getQuery();
+
+ if (!parameters.isEmpty()) {
+ if (query != null) {
+ query += "&";
+ } else {
+ query = "";
+ }
+ for (NameValuePair parameter : parameters) {
+ String name = parameter.getName();
+ String value = parameter.getValue();
+
+ query += String.format("%s=%s&", name, value);
+ }
+ }
+ return query;
+ }
+
+ private String extractPath(URI target) throws Exception {
+ String pathOverride = override.get();
+
+ if (pathOverride == null) {
+ String targetPath = target.getPath();
+
+ if (targetPath != null) {
+ return targetPath;
+ }
+ return template.getPath();
+ }
+ return pathOverride;
+ }
+
+ private int extractPort(URI target) throws Exception {
+ String targetHost = target.getHost();
+ int targetPort = target.getPort();
+
+ if (targetHost != null) {
+ return targetPort;
+ }
+ return template.getPort();
+ }
+
+ private String extractHost(URI target) throws Exception {
+ String targetHost = target.getHost();
+
+ if (targetHost != null) {
+ return targetHost;
+ }
+ return template.getHost();
+ }
+
+ private String extractScheme(URI target) throws Exception {
+ String targetScheme = target.getScheme();
+
+ if (targetScheme != null) {
+ return targetScheme;
+ }
+ return template.getScheme();
+ }
+
+ private HttpEntity createEntity() throws Exception {
+ List parameters = createParameters();
+
+ if(parameters.isEmpty()) {
+ String content = body.get();
+ return new StringEntity(content, "UTF-8");
+ }
+ return new UrlEncodedFormEntity(parameters, "UTF-8");
+ }
+
+ private List createParameters() throws Exception {
+ List list = new LinkedList();
+
+ if (!parameters.isEmpty()) {
+ Set names = parameters.keySet();
+
+ for (String name : names) {
+ String value = parameters.get(name);
+ NameValuePair pair = createAttribute(name, value);
+
+ list.add(pair);
+ }
+ }
+ return list;
+ }
+
+ private NameValuePair createAttribute(String name, String value) {
+ return new BasicNameValuePair(name, value);
+ }
+
+ private Credentials createCredentials(String user, String password) {
+ return new UsernamePasswordCredentials(user, password);
+ }
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestHandler.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestHandler.java
new file mode 100644
index 0000000..bf28e8b
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestHandler.java
@@ -0,0 +1,8 @@
+package org.simpleframework.demo.rest;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+
+public interface RequestHandler {
+ void handle(Request request, Response response, T message) throws Exception;
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestProcessor.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestProcessor.java
new file mode 100644
index 0000000..44de650
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestProcessor.java
@@ -0,0 +1,80 @@
+package org.simpleframework.demo.rest;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.simpleframework.http.Path;
+import org.simpleframework.http.Protocol;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.core.Container;
+import org.simpleframework.http.core.ContainerSocketProcessor;
+import org.simpleframework.transport.SocketProcessor;
+import org.simpleframework.transport.connect.Connection;
+import org.simpleframework.transport.connect.SocketConnection;
+
+import com.google.gson.Gson;
+
+public class RequestProcessor {
+
+ private final Map registrations;
+ private final SocketProcessor processor;
+ private final SocketAddress address;
+ private final Container container;
+ private final Connection connection;
+ private final Gson gson;
+
+ public RequestProcessor(int port) throws Exception {
+ this.registrations = new ConcurrentHashMap();
+ this.address = new InetSocketAddress(port);
+ this.container = new RequestRouter();
+ this.processor = new ContainerSocketProcessor(container);
+ this.connection = new SocketConnection(processor);
+ this.gson = new Gson();
+ }
+
+ public void register(RequestRegistration registration) throws Exception {
+ String prefix = registration.getPrefix();
+ RequestRegistration previous = registrations.put(prefix, registration);
+
+ if(previous != null) {
+ throw new IllegalArgumentException("Prefix '" + prefix + "' has already been used");
+ }
+ }
+
+ public void start() throws Exception {
+ connection.connect(address);
+ }
+
+ public void stop() throws Exception {
+ connection.close();
+ }
+
+ private class RequestRouter implements Container {
+
+
+ @Override
+ public void handle(Request request, Response response) {
+ try {
+ Path path = request.getPath();
+ String normal = path.getPath();
+ RequestRegistration registration = registrations.get(normal);
+ RequestHandler handler = registration.getHandler();
+ Class type = registration.getType();
+ String content = request.getContent();
+ Object message = gson.fromJson(content, type);
+ long time = System.currentTimeMillis();
+
+ response.setDate(Protocol.DATE, time);
+ handler.handle(request, response, message);
+ response.close();
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ }
+
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestRegistration.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestRegistration.java
new file mode 100644
index 0000000..23aee51
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestRegistration.java
@@ -0,0 +1,30 @@
+package org.simpleframework.demo.rest;
+
+public class RequestRegistration {
+
+ private final RequestHandler handler;
+ private final Class type;
+ private final String prefix;
+
+ public RequestRegistration(String prefix, RequestHandler handler) {
+ this(prefix, handler, null);
+ }
+
+ public RequestRegistration(String prefix, RequestHandler handler, Class type) {
+ this.handler = handler;
+ this.type = type;
+ this.prefix = prefix;
+ }
+
+ public RequestHandler getHandler() {
+ return handler;
+ }
+
+ public Class getType() {
+ return type;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestRouter.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestRouter.java
new file mode 100644
index 0000000..bffbe85
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/RequestRouter.java
@@ -0,0 +1,5 @@
+package org.simpleframework.demo.rest;
+
+public interface RequestRouter {
+ void register(RequestHandler handler, Class type, String prefix);
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ResponseConverter.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ResponseConverter.java
new file mode 100644
index 0000000..3a46291
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/ResponseConverter.java
@@ -0,0 +1,102 @@
+package org.simpleframework.demo.rest;
+
+public class ResponseConverter {
+
+ public ResponseConverter() {
+ super();
+ }
+
+ public boolean accept(Class type) {
+ Class actual = convert(type);
+
+ if (actual == String.class) {
+ return true;
+ }
+ if (actual == Integer.class) {
+ return true;
+ }
+ if (actual == Double.class) {
+ return true;
+ }
+ if (actual == Float.class) {
+ return true;
+ }
+ if (actual == Boolean.class) {
+ return true;
+ }
+ if (actual == Byte.class) {
+ return true;
+ }
+ if (actual == Short.class) {
+ return true;
+ }
+ if (actual == Long.class) {
+ return true;
+ }
+ if (actual == Character.class) {
+ return true;
+ }
+ return false;
+ }
+
+ public Object convert(Class type, String value) {
+ Class actual = convert(type);
+
+ if (actual == String.class) {
+ return value;
+ }
+ if (actual == Integer.class) {
+ return Integer.parseInt(value);
+ }
+ if (actual == Double.class) {
+ return Double.parseDouble(value);
+ }
+ if (actual == Float.class) {
+ return Float.parseFloat(value);
+ }
+ if (actual == Boolean.class) {
+ return Boolean.parseBoolean(value);
+ }
+ if (actual == Byte.class) {
+ return Byte.parseByte(value);
+ }
+ if (actual == Short.class) {
+ return Short.parseShort(value);
+ }
+ if (actual == Long.class) {
+ return Long.parseLong(value);
+ }
+ if (actual == Character.class) {
+ return value.charAt(0);
+ }
+ return value;
+ }
+
+ private Class convert(Class type) {
+ if (type == int.class) {
+ return Integer.class;
+ }
+ if (type == double.class) {
+ return Double.class;
+ }
+ if (type == float.class) {
+ return Float.class;
+ }
+ if (type == boolean.class) {
+ return Boolean.class;
+ }
+ if (type == byte.class) {
+ return Byte.class;
+ }
+ if (type == short.class) {
+ return Short.class;
+ }
+ if (type == long.class) {
+ return Long.class;
+ }
+ if (type == char.class) {
+ return Character.class;
+ }
+ return type;
+ }
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/StatusResponse.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/StatusResponse.java
new file mode 100644
index 0000000..db99021
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/StatusResponse.java
@@ -0,0 +1,20 @@
+package org.simpleframework.demo.rest;
+
+public class StatusResponse {
+
+ private final String address;
+ private final boolean success;
+
+ public StatusResponse(String address, boolean success) {
+ this.address = address;
+ this.success = success;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/SubscribeRequest.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/SubscribeRequest.java
new file mode 100644
index 0000000..a88f51e
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/SubscribeRequest.java
@@ -0,0 +1,32 @@
+package org.simpleframework.demo.rest;
+
+public class SubscribeRequest {
+
+ private final String key;
+ private final String filter;
+ private final String address;
+ private final long duration;
+
+ public SubscribeRequest(String key, String filter, String address, long duration){
+ this.filter = filter;
+ this.address = address;
+ this.duration = duration;
+ this.key = key;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getFilter() {
+ return filter;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public long getDuration() {
+ return duration;
+ }
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/SubscribeRequestHandler.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/SubscribeRequestHandler.java
new file mode 100644
index 0000000..836e3cc
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/SubscribeRequestHandler.java
@@ -0,0 +1,38 @@
+package org.simpleframework.demo.rest;
+
+import java.io.OutputStream;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+
+import com.google.gson.Gson;
+
+public class SubscribeRequestHandler implements RequestHandler {
+
+ private final SubscriptionManager manager;
+ private final Gson gson;
+
+ public SubscribeRequestHandler(SubscriptionManager manager) {
+ this.gson = new Gson();
+ this.manager = manager;
+ }
+
+ @Override
+ public void handle(Request request, Response response, SubscribeRequest message) throws Exception {
+ String key = message.getKey();
+ StatusResponse status = new StatusResponse(key, true);
+ manager.subscribe(message);
+ String content = gson.toJson(status);
+ OutputStream output = response.getOutputStream();
+ byte[] data = content.getBytes("UTF-8");
+
+ response.setContentType("text/json");
+ response.setContentLength(data.length);
+ output.write(data);
+ output.flush();
+ response.close();
+
+
+ }
+
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/SubscriptionManager.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/SubscriptionManager.java
new file mode 100644
index 0000000..edcd158
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/SubscriptionManager.java
@@ -0,0 +1,9 @@
+package org.simpleframework.demo.rest;
+
+import java.util.List;
+
+public interface SubscriptionManager {
+ List match(MatchRequest request);
+ void subscribe(SubscribeRequest request);
+ void renew(RenewRequest request);
+}
diff --git a/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/SubscriptionMessagePublisher.java b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/SubscriptionMessagePublisher.java
new file mode 100644
index 0000000..70999b7
--- /dev/null
+++ b/simple-demo/simple-demo-rest/src/main/java/org/simpleframework/demo/rest/SubscriptionMessagePublisher.java
@@ -0,0 +1,35 @@
+package org.simpleframework.demo.rest;
+
+import java.util.List;
+
+import org.simpleframework.http.Method;
+
+import com.google.gson.Gson;
+
+public class SubscriptionMessagePublisher implements MessagePublisher {
+
+ private final SubscriptionManager manager;
+ private final Gson gson;
+
+ public SubscriptionMessagePublisher(SubscriptionManager manager) {
+ this.gson = new Gson();
+ this.manager = manager;
+ }
+
+ @Override
+ public void publish(Object value) throws Exception {
+ Class type = value.getClass();
+ MatchRequest request = new MatchRequest(type);
+ List matches = manager.match(request);
+ String data = gson.toJson(value);
+
+ for(String address : matches) {
+ RequestBuilder builder = new RequestBuilder(address);
+
+ builder.setMethod(Method.POST);
+ builder.setBody(data);
+ builder.execute(String.class);
+ }
+ }
+
+}