Skip to content

Instantly share code, notes, and snippets.

@contextfw
Created April 26, 2011 15:53
Show Gist options
  • Select an option

  • Save contextfw/942528 to your computer and use it in GitHub Desktop.

Select an option

Save contextfw/942528 to your computer and use it in GitHub Desktop.
Example: Chat
public class Chat extends Component implements ChatServiceClient {
@Inject
private ChatService service;
private long lastAccess = -1;
@Element
public List<ChatMsg> messages() {
return service.getMessages();
}
@AfterBuild
public void afterBuild() {
lastAccess = service.getLastAccess();
}
@Attribute
public boolean changed() {
return service.getLastAccess() > lastAccess;
}
@Remoted
@Delayed(ChatService.class)
public void checkMessages() {
System.out.println("Checking messages");
refresh();
}
@ScriptElement(onCreate=false)
public Script jsUpdate() {
return new JsUpdate(this, "checkMessages");
}
@ScriptElement(onUpdate=false)
public Script jsInit() {
return new JsInit(this);
}
}
Chat = Component.extend({
init : function(id) {
this._super(id);
var self = this;
setTimeout(function() { self.checkMessages() }, 1000);
},
checkMessages : function() {
this.call("checkMessages")();
}
});
ChatInput = Component.extend({
init : function(id) {
var self = this;
this._super(id);
this.attach();
},
attach : function() {
var self = this;
this.el("newmsg").keypress(function(e) {
if(e.which==13) {
e.preventDefault();
self.addMessage();
}
});
this.el("button").click(function() {
self.addMessage()
});
},
addMessage : function() {
var self = this;
var msg = this.el("newmsg");
this.call("addMessage", null, function() {
self.el("newmsg").focus();
})(msg.val());
}
});
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Chat">
<div id="{@id}">
<xsl:call-template name="Chat" />
</div>
</xsl:template>
<xsl:template match="Chat.update">
<xsl:if test="@changed='true'">
<replaceInner id="{@id}">
<xsl:call-template name="Chat" />
</replaceInner>
</xsl:if>
</xsl:template>
<xsl:template name="Chat">
<ul>
<xsl:for-each select="messages/ChatMsg">
<li>
<b><xsl:value-of select="@sent" /></b>:
<xsl:value-of select="msg" />
<i> (expires: <xsl:value-of select="@expires" />)</i>
</li>
</xsl:for-each>
</ul>
</xsl:template>
</xsl:stylesheet>
public class ChatInput extends Component {
@Inject
private ChatService service;
@Remoted
public void addMessage(String msg) {
service.addMessage(msg);
refresh();
}
@ScriptElement
public Script init() {
return new ComponentFunctionCall(this, "init");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="ChatInput">
<div id="{@id}">
<xsl:call-template name="ChatInput" />
</div>
</xsl:template>
<xsl:template match="ChatInput.update">
<replaceInner id="{@id}">
<xsl:call-template name="ChatInput" />
</replaceInner>
</xsl:template>
<xsl:template name="ChatInput">
<input type="text" id="{@id}_newmsg" />
<input id="{@id}_button" type="button"
class="button"
value="Add (or press enter)" />
</xsl:template>
</xsl:stylesheet>
@Singleton
public class ChatService implements DelayedUpdateHandler<ChatServiceClient>, Runnable {
private final LinkedList<ChatMsg> messages = new LinkedList<ChatMsg>();
private final static long expiration = 1000 * 25;
private long lastAccess = System.currentTimeMillis();
private final Set<Continuation> continuations = new HashSet<Continuation>();
private ScheduledThreadPoolExecutor invoker = new ScheduledThreadPoolExecutor(1);
public List<ChatMsg> getMessages() {
return Collections.unmodifiableList(messages);
}
private void schedule(ChatMsg msg) {
invoker.schedule(this, msg.getExpires().getMillis() - System.currentTimeMillis(),
TimeUnit.MILLISECONDS);
}
public void addMessage(String value) {
ChatMsg msg = new ChatMsg(
new DateTime(System.currentTimeMillis()),
new DateTime(System.currentTimeMillis() + expiration),
value);
synchronized (continuations) {
if (messages.size() > 9) {
invoker.remove(this);
messages.removeFirst();
}
messages.add(msg);
schedule(messages.getFirst());
notifyChats();
}
}
private void notifyChats() {
lastAccess = System.currentTimeMillis();
synchronized (continuations) {
Iterator<Continuation> iter = continuations.iterator();
while (iter.hasNext()) {
// System.out.println("Has next");
try {
iter.next().resume();
} catch (Exception e) {
// Just swallow it
} finally {
iter.remove();
}
}
}
}
@Override
public boolean isUpdateDelayed(ChatServiceClient chat, HttpServletRequest request) {
Continuation continuation = ContinuationSupport
.getContinuation(request);
synchronized (continuations) {
if (continuations.contains(continuation)) {
continuations.remove(continuation);
return false;
} else {
if (chat.changed()) {
return false;
} else {
continuations.add(continuation);
continuation.suspend();
return true;
}
}
}
}
public long getLastAccess() {
return lastAccess;
}
@Override
public void run() {
synchronized (continuations) {
if (messages.size() > 0) {
while (messages.size() > 0
&& messages.getFirst().getExpires().getMillis() <= System
.currentTimeMillis()) {
messages.removeFirst();
}
notifyChats();
}
if (messages.size() > 0) {
schedule(messages.getFirst());
}
}
}
}
public interface ChatServiceClient {
boolean changed();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment