You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@esme.apache.org by dp...@apache.org on 2009/01/23 01:26:47 UTC
svn commit: r736864 [1/3] - in /incubator/esme/trunk/server: ./
src/main/scala/bootstrap/liftweb/ src/main/scala/org/
src/main/scala/org/apache/ src/main/scala/org/apache/esme/
src/main/scala/org/apache/esme/actor/ src/main/scala/org/apache/esme/api/
s...
Author: dpp
Date: Thu Jan 22 16:26:46 2009
New Revision: 736864
URL: http://svn.apache.org/viewvc?rev=736864&view=rev
Log:
Moved to org.apache package space
Added:
incubator/esme/trunk/server/src/main/scala/org/
incubator/esme/trunk/server/src/main/scala/org/apache/
incubator/esme/trunk/server/src/main/scala/org/apache/esme/
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/Distributor.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/GroupActor.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/HttpSender.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/MessagePullActor.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/SchedulerActor.scala (with props)
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/
incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/RestAPI.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/
incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/.keep
incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/PublicTimeline.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/TagCloud.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/Timeline.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/
incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/AtomFeed.scala (with props)
incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/Feed.scala (with props)
incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/RssFeed.scala (with props)
incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/TwitterFeed.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/
incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/MsgFormat.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/MsgParser.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/TagUtils.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/.keep
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Action.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/AuthToken.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/DidPerform.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/ExtSession.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Group.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Mailbox.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Message.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/MessageTag.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Relationship.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Tag.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Tracking.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/UrlStore.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/User.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/
incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/.keep
incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/Style.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/UserSnip.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/
incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/.keep
incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/ActionView.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/Auth.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/Track.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/UserView.scala
incubator/esme/trunk/server/src/test/scala/org/
incubator/esme/trunk/server/src/test/scala/org/apache/
incubator/esme/trunk/server/src/test/scala/org/apache/esme/
incubator/esme/trunk/server/src/test/scala/org/apache/esme/AppTest.scala
incubator/esme/trunk/server/src/test/scala/org/apache/esme/lib/
incubator/esme/trunk/server/src/test/scala/org/apache/esme/lib/MsgParseTest.scala
Removed:
incubator/esme/trunk/server/src/main/scala/us/
incubator/esme/trunk/server/src/test/scala/us/
Modified:
incubator/esme/trunk/server/pom.xml
incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala
Modified: incubator/esme/trunk/server/pom.xml
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/pom.xml?rev=736864&r1=736863&r2=736864&view=diff
==============================================================================
--- incubator/esme/trunk/server/pom.xml (original)
+++ incubator/esme/trunk/server/pom.xml Thu Jan 22 16:26:46 2009
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
- <groupId>us.esme</groupId>
+ <groupId>org.apache</groupId>
<artifactId>esme</artifactId>
<version>0.2.3-SNAPSHOT</version>
<packaging>war</packaging>
Modified: incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala?rev=736864&r1=736863&r2=736864&view=diff
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala (original)
+++ incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala Thu Jan 22 16:26:46 2009
@@ -1,19 +1,24 @@
package bootstrap.liftweb
-/*
- * Copyright 2008 WorldWide Conferencing, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions
- * and limitations under the License.
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
*/
import net.liftweb.util._
@@ -23,7 +28,7 @@
import Helpers._
import net.liftweb.mapper.{DB, ConnectionManager, Schemifier, DefaultConnectionIdentifier, ConnectionIdentifier}
import java.sql.{Connection, DriverManager}
-import us.esme._
+import org.apache.esme._
import model._
import actor._
import lib._
@@ -45,7 +50,7 @@
if (!DB.jndiJdbcConnAvailable_?) DB.defineConnectionManager(DefaultConnectionIdentifier, DBVendor)
// where to search snippet
- LiftRules.addToPackages("us.esme")
+ LiftRules.addToPackages("org.apache.esme")
if (Props.mode == Props.RunModes.Test) {
Schemifier.destroyTables_!!(Log.infoF _, User, ExtSession,
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/Distributor.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/Distributor.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/Distributor.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/Distributor.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,148 @@
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * Distributor.scala
+ *
+ * To change this template, choose Tools | Template Manager
+ * and open the template in the editor.
+ */
+
+package org.apache.esme.actor
+
+import scala.actors.Actor
+import Actor._
+
+import net.liftweb._
+import http._
+import util._
+
+import org.apache.esme._
+import model._
+
+import scala.xml.{Elem}
+
+object Distributor extends Actor {
+ def act = loop {
+ react {
+ case StartMeUp =>
+ link(ActorWatcher)
+ User.findAll.map(_.id.is).foreach(findOrCreateUser)
+ Group.findAll.map(_.id.is).foreach(findOrCreateGroup)
+
+ case UserCreatedMessage(user, text, tags, when,
+ metaData,
+ source,
+ inReplyTo) =>
+ findOrCreateUser(user) !
+ UserActor.CreateMessage(text, tags,
+ when, metaData, source, inReplyTo)
+
+ case AddMessageToMailbox(user, message, reason) =>
+ findOrCreateUser(user) ! UserActor.AddToMailbox(message, reason)
+
+ case Listen(user, who) =>
+ findOrCreateUser(user) ! UserActor.Listen(who)
+
+ case Unlisten(user, who) =>
+ findOrCreateUser(user) ! UserActor.Unlisten(who)
+
+ case LatestMessages(user, cnt) =>
+ findOrCreateUser(user).forward(UserActor.LatestMessages(cnt))
+
+ case m @ UserUpdated(id) =>
+ users.get(id).foreach(_ ! m)
+
+ case nm @ NewMessage(msg) =>
+ val toSend = UserActor.TestForTracking(msg)
+ users.values.foreach(_ ! toSend)
+ listeners.foreach(_ ! nm)
+
+ case UpdateTrackingFor(userId, ttype) =>
+ for (user <- users.get(userId))
+ user ! UserActor.UpdateTracking(ttype)
+
+ case PublicTimelineListeners(who) =>
+ listeners = who :: listeners
+
+ case PublicTimelineUnlisteners(who) =>
+ listeners = listeners.filter(_ ne who)
+
+ case _ =>
+ }
+ }
+
+ private case object StartMeUp
+
+ case class UserCreatedMessage(user: Long, text: String, tags: List[String],
+ when: Long,
+ metaData: Box[Elem],
+ source: String,
+ replyTo: Box[Long])
+ case class AddMessageToMailbox(user: Long, message: Message, reason: MailboxReason)
+ case class Listen(user: Long, who: Actor)
+ case class Unlisten(user: Long, who: Actor)
+ case class LatestMessages(user: Long, cnt: Int)
+ case class NewMessage(msg: Message)
+ case class UpdateTrackingFor(user: Long, which: TrackingType)
+ case class UserUpdated(userId: Long)
+ case class PublicTimelineListeners(who: Actor)
+ case class PublicTimelineUnlisteners(who: Actor)
+ sealed trait TrackingType
+ case object PerformTrackingType extends TrackingType
+ case object TrackTrackingType extends TrackingType
+
+ start
+ this ! StartMeUp
+
+ // do nothing
+ def touch {
+
+ }
+
+ private var users: Map[Long, UserActor] = Map.empty
+ private var groups: Map[Long, GroupActor] = Map.empty
+ private var listeners: List[Actor] = Nil
+
+ private def findOrCreateUser(user: Long): UserActor = {
+ users.get(user) match {
+ case Some(ret) => ret
+ case _ =>
+ val ret = new UserActor
+ ret.start
+ ret ! UserActor.StartMeUp(user)
+ users = users + (user -> ret)
+ ret
+ }
+ }
+
+ private def findOrCreateGroup(group: Long) {
+ groups.get(group) match {
+ case Some(ret) => ret
+ case _ =>
+ val ret = new GroupActor
+ ret.start
+ ret ! GroupActor.StartMeUp(group)
+ groups = groups + (group -> ret)
+ ret
+ }
+ }
+}
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/GroupActor.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/GroupActor.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/GroupActor.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/GroupActor.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.esme.actor
+
+import scala.actors.Actor
+import Actor._
+
+
+import net.liftweb._
+import http._
+
+import org.apache.esme._
+import model._
+
+object GroupActor {
+ case class StartMeUp(group: Long)
+}
+
+class GroupActor extends Actor {
+ import GroupActor._
+
+ def act = loop {
+ react {
+ case StartMeUp =>
+ link(ActorWatcher)
+ }
+ }
+}
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/HttpSender.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/HttpSender.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/HttpSender.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/HttpSender.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,147 @@
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.esme.actor
+
+import scala.actors.Actor
+import Actor._
+
+import net.liftweb._
+import http._
+import util._
+import net.liftweb.http.testing._
+
+import org.apache.esme._
+import model._
+import lib._
+
+import org.apache.commons.httpclient._
+import org.apache.commons.httpclient.auth._
+import methods._
+import java.io.OutputStream
+
+object HttpSender extends Actor with GetPoster {
+ def act = loop {
+ react {
+ case StartMeUp =>
+ link(ActorWatcher)
+
+
+ case SendAMessage(action, msg, user, reason, token) =>
+ send(action, msg, user, reason, token)
+
+ case _ =>
+ }
+ }
+
+ private case object StartMeUp
+ case class SendAMessage(action: Performances, msg: Message, user: User, reason: MailboxReason, token: String)
+
+ private def send(action: Performances, msg: Message, user: User, reason: MailboxReason, token: String) {
+ import Mailer._
+
+ action match {
+ case MailTo(who, text) =>
+ val body = text match {
+ case None => XHTMLMailBodyType(msg.digestedXHTML)
+ case Some(t) => PlainMailBodyType(expandText(t, msg, user, reason))
+ }
+ Mailer.sendMail(From("i@esme.us"), Subject("msg"),
+ To(who), body)
+
+ case HttpTo(url, username, password, headers, data) =>
+ val load = data match {
+ case None => ""
+ case Some(d) => expandText(d, msg, user, reason)
+ }
+ post(url, httpClient,
+ ("X-ESME-Token" -> token) :: headers,
+ username, password,
+ load)
+
+ case PerformResend | PerformFilter => // IGNORE
+
+ }
+ }
+
+ private def expandText(text: String, msg: Message, user: User, reason: MailboxReason) = {
+ val followerId = reason match {
+ case FollowedReason(followerId) => Some(followerId)
+ case UnfollowedReason(followerId) => Some(followerId)
+ case _ => None
+ }
+
+ val followerName = followerId match {
+ case Some(followerId) => User.find(followerId).map[String](_ nickname).openOr("N/A")
+ case None => "N/A"
+ }
+
+ text.replace("%u", user.nickname).
+ replace("%f", followerName).
+ replace("%i", user.imageUrl).
+ replace("%w", user.wholeName).
+ replace("%s", msg.getText)
+ }
+
+ // Overloaded method from GetPoster
+ private def post(url: String, httpClient: HttpClient,
+ headers: List[(String, String)],
+ username: String, password: String,
+ body: String) {
+ val poster = new PostMethod(baseUrl + url)
+ for ((name, value) <- headers) poster.setRequestHeader(name, value)
+ poster.setRequestEntity(new RequestEntity {
+ private val bytes = body.toString.getBytes("UTF-8")
+
+ def getContentLength() = bytes.length
+ def getContentType() = "application/x-www-form-urlencoded"
+ def isRepeatable() = true
+ def writeRequest(out: OutputStream) {
+ out.write(bytes)
+ }
+ })
+
+ httpClient.getState().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password))
+ poster.setDoAuthentication(true)
+
+ try {
+ httpClient.executeMethod(poster)
+ Log.info(poster.getStatusText)
+ Log.info(poster.getResponseBodyAsString)
+ } finally {
+ poster.releaseConnection
+ }
+ }
+
+ def httpClient = {
+ val ret = new HttpClient(new SimpleHttpConnectionManager(false))
+
+ for (host <- Props.get("http.proxyHost"))
+ ret.getHostConfiguration.setProxy(host,
+ Props.getInt("http.proxyPort",80))
+ ret
+ }
+
+ start
+ this ! StartMeUp
+
+ def baseUrl = ""
+}
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/MessagePullActor.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/MessagePullActor.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/MessagePullActor.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/MessagePullActor.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,111 @@
+package org.apache.esme.actor
+
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import scala.actors.Actor
+import scala.actors.Actor._
+import net.liftweb.http.ActorWatcher
+import org.apache.esme.actor.Distributor.{UserCreatedMessage=>Msg}
+
+class MessagePullActor(val messageProcessor: Actor, private var lastMessage: Option[Msg], val messageSource: UniqueMessageSource) extends Actor {
+
+ import MessagePullActor._
+
+ def act {
+ loop {
+ react {
+ case StartUp => {
+ link(ActorWatcher)
+ }
+ case ByeBye => {
+ unlink(ActorWatcher)
+ self.exit()
+ }
+ case (msgs: List[Msg]) => {
+ val lastMessages = messageSource.getLastSortedMessages(msgs, lastMessage)
+ for (message <- lastMessages) {
+ messageProcessor ! message
+ lastMessage = Some(message)
+ }
+ }
+ case FetchMessages => actor {
+ // "this" used to reference invoking actor
+ this ! messageSource()
+ }
+ }
+ }
+ }
+
+}
+
+object MessagePullActor extends Actor {
+
+ private var messagePullActors: Map[Any, Actor] = Map()
+
+ def act = loop {
+ react {
+ case StartPullActor(obj, lastMessage, messageSource) => {
+ if (!messagePullActors.contains(obj)) {
+ val pullActor = new MessagePullActor(Distributor, lastMessage, messageSource)
+ messagePullActors += (obj -> pullActor)
+ pullActor.start
+ pullActor ! StartUp
+ }
+ }
+ case StopPullActor(obj) => {
+ if (messagePullActors.contains(obj)) {
+ messagePullActors(obj) ! ByeBye
+ messagePullActors -= obj
+ }
+ }
+ case Fetch(obj) => {
+ if (messagePullActors.contains(obj)) {
+ messagePullActors(obj) ! FetchMessages
+ }
+ }
+ }
+ }
+
+ start
+
+ // do nothing
+ def touch {
+ }
+
+ private case object StartUp
+ private case object ByeBye
+ private case object FetchMessages
+ case class StartPullActor(any: Any, lastMessage: Option[Msg], messageSource: UniqueMessageSource)
+ case class StopPullActor(any: Any)
+ case class Fetch(any: Any)
+}
+
+trait UniqueMessageSource extends (() => List[Msg]) {
+ def messageSorter(a: Msg, b: Msg) = a.when < b.when
+
+ def getLastSortedMessages(msgs: List[Msg], lastMessage: Option[Msg]): List[Msg] = {
+ lastMessage match {
+ case Some(message: Msg) => msgs.filter{messageSorter(message, _)}
+ case None => msgs
+ }
+ }.sort(messageSorter)
+}
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/SchedulerActor.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/SchedulerActor.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/SchedulerActor.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/SchedulerActor.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,108 @@
+package org.apache.esme.actor
+
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import scala.actors.Actor
+import scala.actors.TIMEOUT
+import scala.actors.Actor._
+import org.apache.esme.actor.Distributor.AddMessageToMailbox
+import org.apache.esme.model._
+import net.liftweb.http.ActorWatcher
+import net.liftweb.util.{Full,Empty,TimeHelpers}
+
+class SchedulerActor(val messageProcessor: Actor, val user: Long, val everySeconds: Int, val reason: MailboxReason) extends Actor {
+
+ import SchedulerActor._
+
+ def act {
+ sendMessage()
+ loop {
+ reactWithin (everySeconds * 1000) {
+ case StartUp => {
+ link(ActorWatcher)
+ }
+ case ByeBye => {
+ unlink(ActorWatcher)
+ self.exit()
+ }
+ case TIMEOUT => {
+ sendMessage()
+ }
+ }
+ }
+ }
+
+ def sendMessage() {
+ val now = System.currentTimeMillis
+ val dateString = TimeHelpers.toInternetDate(now)
+ Message.create.author(user).
+ when(now).
+ source("every").
+ setTextAndTags("Regularly scheduled action: " + dateString, Nil, Empty).
+ foreach{ msg =>
+ // Noisy & can't be rejected
+ // if (msg.save) {
+ messageProcessor ! Distributor.AddMessageToMailbox(user, msg, reason)
+ // }
+ }
+ }
+
+}
+
+object SchedulerActor extends Actor{
+
+ private var regularsPerAction: Map[Long, List[Actor]] = Map()
+
+ def act = loop {
+ react {
+ case StartRegular(action, everySeconds) => {
+ val id = action.id.is
+ val regularActor = new SchedulerActor(Distributor, action.user, everySeconds, RegularReason(id))
+ if (!regularsPerAction.contains(id)) {
+ regularsPerAction += (id -> List(regularActor))
+ } else {
+ regularsPerAction(id) += regularActor
+ }
+ regularActor.start
+ regularActor ! StartUp
+ }
+
+ case StopRegular(id) => {
+ if (regularsPerAction.contains(id)) {
+ for(regularActor <- regularsPerAction(id)) regularActor ! ByeBye
+ regularsPerAction -= id
+ }
+ }
+ }
+ }
+
+ start
+
+ // do nothing
+ def touch {
+ }
+
+ case class StartRegular(action: Action, everySeconds: Int)
+ case class StopRegular(action: Long)
+ private case object StartUp
+ private case object ByeBye
+}
Propchange: incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/SchedulerActor.scala
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,228 @@
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.esme.actor
+
+import scala.actors.Actor
+import Actor._
+
+import net.liftweb._
+import http._
+import util._
+import mapper._
+
+import org.apache.esme._
+import model._
+import lib._
+
+import java.util.logging._
+import java.util.{TimeZone, Calendar}
+import scala.xml.{Elem}
+
+object UserActor {
+ private[actor] case class StartMeUp(user: Long)
+ private[actor] case class CreateMessage(text: String, tags: List[String],
+ when: Long, metaData: Box[Elem],
+ source: String,
+ replyTo: Box[Long])
+ private[actor] case class AddToMailbox(msg: Message, reason: MailboxReason)
+ private[actor] case class Listen(who: Actor)
+ private[actor] case class Unlisten(who: Actor)
+ private[actor] case class LatestMessages(cnt: Int)
+ private[actor] case class TestForTracking(msg: Message)
+ private[actor] case class UpdateTracking(ttype: Distributor.TrackingType)
+
+ case class MessageReceived(msg: Message, reason: MailboxReason)
+
+ val logger: Logger = Logger.getLogger("org.apache.esme.actor")
+}
+
+class UserActor extends Actor {
+ import UserActor._
+
+ private var userId: Long = 0
+
+ private var userTimezone: TimeZone = _
+
+ private var listeners: List[Actor] = Nil
+
+ private var tracking: List[TrackingMatcher] = Nil
+
+ private var perform: List[PerformMatcher] = Nil
+
+ private var _mailbox: Array[Long] = Array()
+
+ private def followers: List[Long] = User.followerIdsForUserId(userId)
+
+ private case class RunFunc(f: () => Unit)
+
+ def act = loop {
+ react {
+ case m @ Distributor.UserUpdated(_) =>
+ User.find(userId).
+ foreach(u => userTimezone = TimeZone.getTimeZone(u.timezone))
+ listeners.foreach(_ ! m)
+
+ case StartMeUp(user) =>
+ link(ActorWatcher)
+ userId = user
+ User.find(userId).
+ foreach(u => userTimezone = TimeZone.getTimeZone(u.timezone))
+
+ _mailbox = Mailbox.mostRecentMessagesFor(userId, 500).
+ map(_._1.id.is).toArray
+
+ this ! UpdateTracking(Distributor.TrackTrackingType)
+ this ! UpdateTracking(Distributor.PerformTrackingType)
+
+ case RunFunc(f) => f()
+
+ case CreateMessage(text, tags, when, metaData, source, replyTo) =>
+ val tagLst = tags.removeDuplicates.map(Tag.findOrCreate)
+
+ Message.create.author(userId).when(when).
+ source(source).
+ setTextAndTags(text, tagLst, metaData).map{msg =>
+ // do some security... only reply to messages
+ // that are in our mailbox
+ for (rt <- replyTo;
+ mb <- Mailbox.find(By(Mailbox.message, rt),
+ By(Mailbox.user, userId))) msg.replyTo(rt)
+
+ msg.saveMe
+
+ Distributor ! Distributor.AddMessageToMailbox(userId, msg, NoReason)
+
+ for (id <- followers)
+ Distributor ! Distributor.AddMessageToMailbox(id, msg, NoReason)
+
+ for (id <- msg.sentToIds)
+ Distributor ! Distributor.AddMessageToMailbox(id, msg,
+ DirectReason(userId))
+
+ for (convId <- msg.conversation.can ;
+ val msgId = Message.findMap(By(Message.conversation, convId))
+ (m => Full(m.id.is));
+ userId <- (Mailbox.findMap(InRaw(Mailbox.message, msgId.mkString(", "),
+ IHaveValidatedThisSQL("dpp", "Aug 27. 2008")))
+ (mb => Full(mb.user.is))).removeDuplicates)
+ Distributor ! Distributor.AddMessageToMailbox(userId, msg, ConversationReason(convId))
+
+ Distributor ! Distributor.NewMessage(msg)
+ }
+
+ case AddToMailbox(msg, reason) =>
+ addToMailbox(msg, reason)
+
+
+ case TestForTracking(msg) =>
+ for (t <- tracking.find(_.doesMatch_?(msg)))
+ this ! AddToMailbox(msg, TrackReason(t.trackId))
+
+
+ case UpdateTracking(ttype) =>
+ ttype match {
+ case Distributor.TrackTrackingType =>
+ tracking = Tracking.findAll(By(Tracking.user, userId),
+ By(Tracking.disabled, false),
+ By(Tracking.removed, false)).
+ flatMap(_.matcher)
+
+ case Distributor.PerformTrackingType =>
+ perform = Action.findAll(By(Action.user, userId),
+ By(Action.disabled, false),
+ By(Action.removed, false)).
+ map(_.matcher)
+ }
+
+ case Listen(who) => listeners = who :: listeners
+
+ case Unlisten(who) => listeners = listeners.filter(_ ne who)
+
+ case LatestMessages(cnt) => reply(_mailbox.take(cnt).toList)
+ }
+ }
+
+ def buildCalendar = userTimezone match {
+ case null => Calendar.getInstance()
+ case tz => Calendar.getInstance(tz)
+ }
+
+ private def addToMailbox(msg: Message, reason: MailboxReason) {
+ // if the message is not in my mailbox
+ if (Mailbox.find(By(Mailbox.message, msg),
+ By(Mailbox.user, userId)).isEmpty) {
+
+ // get all the performance things
+ val cal = buildCalendar
+ val toDo = perform.filter(_.func(msg, userId, cal, reason))
+
+ // is one of those reasons rejection of the message
+ val reject = toDo.exists(_.filter_?)
+
+ // if we're not rejecting the message
+ if (!reject) {
+ val mb = Mailbox.create.user(userId).message(msg)
+ reason match {
+ case TrackReason(trackId) => mb.viaTrack(trackId)
+ case DirectReason(fromId) => mb.directlyFrom(fromId)
+ case ConversationReason(convId) => mb.conversation(convId)
+ case ResendReason(resender) => mb.resentBy(resender)
+ case LoginReason(loggedId) => mb.login(loggedId)
+ case FollowedReason(followerId) => mb.followed(followerId)
+ case UnfollowedReason(unfollowerId) => mb.unfollowed(unfollowerId)
+ case ProfileReason(moduserId) => mb.profile(moduserId)
+ case RegularReason(actionId) => mb.regular(actionId)
+ case NoReason =>
+ }
+ mb.saveMe
+
+ _mailbox = (msg.id.is :: _mailbox.toList).take(500).toArray
+
+ listeners.foreach(_ ! MessageReceived(msg, reason))
+
+ toDo.foreach {
+ td =>
+
+ td.whatToDo match {
+ case m @ MailTo(_, _) =>
+ User.find(userId).foreach( u =>
+ HttpSender ! HttpSender.SendAMessage(m, msg, u, reason, td.uniqueId))
+
+ case h @ HttpTo(_, _, _, _, _) =>
+ User.find(userId).foreach( u =>
+ HttpSender ! HttpSender.SendAMessage(h, msg, u, reason, td.uniqueId))
+
+ case PerformResend =>
+ if (! msg.saved_?) msg.save
+ for (id <- followers)
+ Distributor !
+ Distributor.AddMessageToMailbox(id, msg, ResendReason(userId))
+
+ case FetchFeed(url) => MessagePullActor ! MessagePullActor.Fetch(td.performId)
+
+ case PerformFilter => // IGNORE
+ }
+ }
+ }
+ }
+ }
+}
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/RestAPI.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/RestAPI.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/RestAPI.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/RestAPI.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,408 @@
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * RestAPI.scala
+ *
+ * To change this template, choose Tools | Template Manager
+ * and open the template in the editor.
+ */
+
+package org.apache.esme.api
+
+import net.liftweb._
+import http._
+import rest._
+import util._
+import mapper._
+import Helpers._
+
+import org.apache.esme._
+import model._
+import actor._
+
+import scala.xml.{NodeSeq, Text, Elem, XML}
+import scala.actors.Actor
+import Actor._
+
+import scala.collection.mutable.ListBuffer
+import java.util.logging._
+
+object RestAPI extends XMLApiHelper {
+ val logger: Logger = Logger.getLogger("org.apache.esme.api")
+
+ def dispatch: LiftRules.DispatchPF = {
+ case Req("api" :: "status" :: Nil, "", GetRequest) => status
+ case Req("api" :: "login" :: Nil, "", PostRequest) => login
+ case Req("api" :: "logout" :: Nil, "", GetRequest) => logout
+ case Req("api" :: "get_msgs" :: Nil, "", GetRequest) => getMsgs
+ case Req("api" :: "wait_for_msgs" :: Nil, "", GetRequest) =>
+ waitForMsgs
+
+ case Req("api" :: "send_msg" :: Nil, "", PostRequest) =>
+ () => sendMsg(User.currentUser.map(_.id.is), S)
+
+ case Req("api" :: "get_following" :: Nil, _, GetRequest) =>
+ following(calcUser)
+ case Req("api" :: "get_followers" :: Nil, _, GetRequest) =>
+ followers(calcUser)
+ case Req("api" :: "follow" :: Nil, _, PostRequest) =>
+ performFollow(S.param("user"))
+ case Req("api" :: "unfollow" :: Nil, _, PostRequest) =>
+ performUnfollow(S.param("user"))
+ case Req("api" :: "all_users" ::
+ Nil, _, GetRequest) => allUsers _
+ case Req("api" :: "get_tagcloud" :: Nil, "", GetRequest) =>
+ () => getTagCloud(S)
+
+ // DPP to document
+ case Req("api" :: "get_tracking" :: Nil, "", GetRequest) =>
+ getTracking
+
+ case Req("api" :: "add_tracking" :: Nil, "", PostRequest) =>
+ addTracking
+
+ case Req("api" :: "remove_tracking" :: Nil, "", PostRequest) =>
+ removeTracking
+
+ case Req("api" :: "get_conversation" :: Nil, "", GetRequest) =>
+ getConversation
+
+ case Req("api" :: "get_actions" :: Nil, "", GetRequest) =>
+ getActions _
+
+ case Req("api" :: "add_action" :: Nil, "", PostRequest) =>
+ addAction _
+
+ case Req("api" :: "enable_action" :: Nil, "", PostRequest) =>
+ enableAction _
+
+ case Req("api" :: "delete_action" :: Nil, "", PostRequest) =>
+ deleteAction _
+ }
+
+ def findAction: Box[Action] =
+ for (user <- User.currentUser ?~ "Not Logged In";
+ id <- S.param("actionid") ?~ "id param not supplied";
+ action <- Action.find(By(Action.user, user),
+ By(Action.id, id.toLong),
+ By(Action.removed, false))) yield action
+
+ def addAction(): LiftResponse = {
+ val ret: Box[NodeSeq] =
+ for (user <- User.currentUser ?~ "Not logged in";
+ name <- S.param("name") ?~ "'name' param not supplied";
+ test <- S.param("test") ?~ "'test' param not supplied";
+ action <- S.param("action") ?~ "'action' param not supplied";
+ val a = Action.create.user(user).name(name);
+ a2 <- a.setTest(test);
+ a3 <- a.setAction(action)) yield a3.saveMe.toXml
+
+ ret
+ }
+
+ def getActions(): LiftResponse = {
+ val ret: Box[NodeSeq] =
+ for (user <- User.currentUser ?~ "Not logged in")
+ yield user.performing.flatMap(_.toXml)
+
+ ret
+ }
+
+ def enableAction(): LiftResponse = {
+ val ret: Box[Boolean] =
+ for (action <- findAction;
+ enabled <- S.param("enabled").map(toBoolean) ?~ "enabled param not supplied")
+ yield action.disabled(!enabled).save
+
+ ret
+ }
+
+ def deleteAction(): LiftResponse = {
+ val ret: Box[Boolean] =
+ for (action <- findAction)
+ yield action.removed(true).save
+
+ ret
+ }
+
+ private def calcUser: Box[User] =
+ S.param("user").flatMap(User.findFromWeb) or
+ User.currentUser
+
+ def getTracking(): LiftResponse = {
+ val ret: Box[NodeSeq] =
+ for (user <- User.currentUser ?~ "Not logged in")
+ yield Tracking.findAll(By(Tracking.user, user)).flatMap(_.toXml)
+ ret
+ }
+
+ def getConversation(): LiftResponse = {
+ val ret: Box[NodeSeq] =
+ for (user <- User.currentUser ?~ "Not logged in";
+ id <- S.param("conversationid").map(toLong) ?~ "id param missing"
+ ) yield <conversation id={id.toString}>{
+ Message.findAndPrime(By(Message.conversation, id),
+ OrderBy(Message.id, Ascending)).map(_.toXml)
+ }</conversation>
+
+ ret
+ }
+
+ def removeTracking(): LiftResponse = {
+ val ret: Box[Boolean] =
+ for (user <- User.currentUser ?~ "Not logged in";
+ id <- S.param("trackid") ?~ "id param missing";
+ track <- Tracking.find(By(Tracking.id, id.toLong),
+ By(Tracking.user, user)) ?~ "Couldn't find tracking item"
+ ) yield track.removed(true).save
+
+ ret
+ }
+
+ def addTracking(): LiftResponse = {
+ val ret: Box[Boolean] =
+ for (user <- User.currentUser ?~ "Not logged in";
+ toTrack <- (S.param("track") ?~ "No track param") if toTrack.trim.length > 0)
+ yield
+ Tracking.create.user(user).regex(toTrack).save
+
+ ret
+ }
+
+ def status(): LiftResponse =
+ {
+ val ret: Box[NodeSeq] = User.currentUser.map(_.toXml)
+ ret
+ }
+
+ def allUsers(): LiftResponse =
+ for (user <- User.findAll) yield user.toXml
+
+
+ def following(muser: Box[User])(): LiftResponse = {
+ val r: Box[NodeSeq] = for (user <- muser) yield
+ user.following().map(_.toXml)
+
+ r
+ }
+
+ def followers(muser: Box[User])(): LiftResponse = {
+ val r: Box[NodeSeq] = for (user <- muser) yield
+ user.followers().map(_.toXml)
+
+ r
+ }
+
+ def performFollow(userName: Box[String])(): LiftResponse = {
+ val r: Box[Boolean] =
+ for (user <- User.currentUser;
+ userName <- userName;
+ other <- User.findFromWeb(userName)
+ ) yield user.follow(other)
+
+ r
+ }
+
+ def performUnfollow(userName: Box[String])(): LiftResponse = {
+ val r: Box[Boolean] =
+ for (user <- User.currentUser;
+ userName <- userName;
+ other <- User.findFromWeb(userName)
+ ) yield user.unfollow(other)
+
+ r
+ }
+
+ def login(): LiftResponse = {
+ val res: Box[Boolean] = if (User.loggedIn_?) Empty else
+ for (token <- S.param("token") ?~ "No 'token' param";
+ auth <- AuthToken.find(By(AuthToken.uniqueId, token))
+ ?~ "Token not found";
+ user <- auth.user.obj;
+ session <- S.session
+ ) yield {
+ User.logUserIn(user)
+ val myActor = buildActor(user.id)
+ restActor(Full(myActor))
+ true
+ }
+
+ res
+ }
+
+ def logout(): LiftResponse = {
+ User.logUserOut()
+ true
+ }
+
+ def waitForMsgs(): LiftResponse = {
+ val seq: Long = Helpers.nextNum
+
+ def waitForAnswer: Box[List[(Message, MailboxReason)]] =
+ receiveWithin(6L * 60L * 1000L) {
+ case (s2, ret: List[(Message, MailboxReason)]) if s2 == seq =>
+ Full(ret)
+ case (s2, _, _) if s2 != seq => waitForAnswer
+ case _ => Empty
+ }
+
+ var r: Box[NodeSeq] =
+ for (act <- restActor.is ?~ "No REST actor";
+ val ignore = act ! ListenFor(self, 5 minutes, seq);
+ answer <- waitForAnswer ?~ "Didn't get an answer")
+ yield answer.flatMap{ case (msg, reason) => msg.toXml % reason.attr}
+
+ r
+ }
+
+ def sendMsgWithToken(req: Req): Box[LiftResponse] = {
+ for (token <- req.param("token");
+ auth <- AuthToken.find(By(AuthToken.uniqueId, token));
+ userId <- auth.user.can;
+ ret <- sendMsg(Full(userId), req)) yield ret
+ }
+
+ def sendMsg(theUser: Box[Long], params: HasParams): LiftResponse = {
+ val r: Box[Boolean] =
+ for (user <- theUser ?~ "User not found";
+ msg <- params.param("message") ?~ "Message not included")
+ yield {
+ val from: String = params.param("via") openOr "api"
+
+ val xml: Box[Elem] = params.param("metadata").flatMap(md =>
+ tryo(XML.loadString(md)))
+
+ Distributor !
+ Distributor.UserCreatedMessage(user, msg,
+ Tag.split(params.param("tags")
+ openOr ""),
+ millis,
+ xml,
+ from,
+ params.param("replyto").map(toLong))
+ true
+ }
+ r
+ }
+
+ def getMsgs(): LiftResponse = {
+ val t: Box[NodeSeq] =
+ for (tagName <- S.param("tag");
+ tag <- Tag.find(By(Tag.name, tagName)))
+ yield tag.findMessages.map(_.toXml)
+
+ val r: Box[NodeSeq] =
+ t or (for (user <- calcUser ?~ "User not found";
+ val lst = Mailbox.mostRecentMessagesFor(user.id, 40))
+ yield lst.flatMap{ case (msg, why) => msg.toXml % why.attr})
+
+ r
+ }
+
+ def getTagCloud(params: HasParams): LiftResponse = {
+ // By default, get the normalised word frequencies from the messages
+ val numTags = (params.param("numTags") openOr "20").toInt
+ val numMsgs = 40 //(params.param("numMsgs") openOr "40").toInt
+
+ val r: Box[NodeSeq] =
+ for (user <- calcUser ?~ "User not found";
+ val lst = Mailbox.mostRecentMessagesFor(user.id, numMsgs).map(_._1))
+ yield
+ <tag_cloud>
+ {
+ for (t <- Tag.centreWeightedTopNTagFreqs(lst, numTags)) yield <tag name={t._1} weight={t._2.toString} />
+ }
+ {
+ for (t <- Message.centreWeightedTopNWordFreqs(lst, numTags)) yield <word name={t._1} weight={t._2.toString} />
+ }
+ </tag_cloud>
+
+
+ r
+ }
+
+ def createTag(in: NodeSeq) = <esme_api>{in}</esme_api>
+
+
+ private def buildActor(userId: Long): RestActor = {
+ val ret = new RestActor
+ ret.start
+ ret ! StartUp(userId)
+ ret
+ }
+
+ object restActor extends SessionVar[Box[RestActor]](Empty) {
+ override def cleanupFunc: Box[() => Unit] = Full(() => this.is.map(_ ! ByeBye))
+ }
+
+
+ class RestActor extends Actor {
+ private var userId: Long = _
+ private var msgs: List[(Message, MailboxReason)] = Nil
+ private var listener: Box[(Actor, Long)] = Empty
+
+ def act = loop {
+ react {
+ case StartUp(userId) =>
+ link(ActorWatcher)
+ this.userId = userId
+ Distributor ! Distributor.Listen(userId, this)
+
+ case ByeBye =>
+ unlink(ActorWatcher)
+ Distributor ! Distributor.Unlisten(userId, this)
+ self.exit()
+
+ case UserActor.MessageReceived(msg, reason) =>
+ msgs = (msg, reason) :: msgs
+ listener.foreach {
+ who =>
+ who._1 ! (who._2, msgs)
+ listener = Empty
+ msgs = Nil
+ }
+
+ case ReleaseListener =>
+ listener.foreach(l => l._1 ! (l._2, Nil))
+ listener = Empty
+
+ case ListenFor(who, len, seq) =>
+ msgs match {
+ case Nil =>
+ listener.foreach(l => l._1 ! (l._2, Nil))
+ listener = Full((who, seq))
+ ActorPing.schedule(this, ReleaseListener, len)
+
+ case xs =>
+ who ! (seq, xs)
+ msgs = Nil
+ listener = Empty
+ }
+ }
+ }
+ }
+
+ private case class StartUp(userId: Long)
+ private case object ByeBye
+ private case class ListenFor(who: Actor, howLong: TimeSpan, seq: Long)
+ private case object ReleaseListener
+}
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/.keep
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/.keep?rev=736864&view=auto
==============================================================================
(empty)
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/PublicTimeline.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/PublicTimeline.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/PublicTimeline.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/PublicTimeline.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,86 @@
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.esme.comet
+
+import net.liftweb.http._
+import net.liftweb.mapper._
+import net.liftweb.util._
+import net.liftweb.util.Helpers._
+import scala.xml._
+import js._
+import JsCmds._
+import JE._
+
+import org.apache.esme._
+import actor._
+import model._
+import lib._
+
+import java.text._
+
+class PublicTimeline extends CometActor {
+ private var messages: List[Long] = Nil
+ private var lastRender = millis
+ private var scheduled = false
+
+ override def localSetup() {
+ super.localSetup()
+ Distributor ! Distributor.PublicTimelineListeners(this)
+ messages = Message.findAll(OrderBy(Message.id, Descending),
+ MaxRows(40)).map(_.id.is)
+ }
+
+ override def localShutdown() {
+ super.localShutdown()
+ Distributor ! Distributor.PublicTimelineUnlisteners(this)
+ }
+
+ def render = {
+ lastRender = millis
+ scheduled = false
+ val msgMap = Message.findMessages(messages)
+ val toDisplay = messages.flatMap(msgMap.get)
+ val jsId = "public_timeline_messages";
+
+ OnLoad(JsCrVar(jsId, JsArray(
+ toDisplay.map(_.asJs) :_*)) &
+ JsFunc("displayMessages", JsVar(jsId), jsId).cmd)
+ }
+
+ override def lowPriority = {
+ case ForceRender =>
+ reRender(false)
+
+ case Distributor.NewMessage(msg) =>
+ messages = (msg.id.is :: messages).take(40)
+
+ if ((millis - lastRender) < 30000L) {
+ if (!scheduled) {
+ scheduled = true
+ ActorPing.schedule(this, ForceRender, 30000L)
+ }
+ }
+ else reRender(false)
+ }
+}
+
+case object ForceRender
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/TagCloud.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/TagCloud.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/TagCloud.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/TagCloud.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,98 @@
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.esme.comet
+
+import net.liftweb.http._
+import net.liftweb.util._
+import net.liftweb.util.Helpers._
+import scala.xml._
+import js._
+import JsCmds._
+import JE._
+
+import org.apache.esme._
+import actor._
+import model._
+import lib._
+
+import java.text._
+
+class TagCloud extends CometActor /* with MsgFormat*/ {
+
+ private var messages: List[Long] = Nil
+
+ override def localSetup() {
+ super.localSetup()
+ for (user <- User.currentUser) {
+ Distributor ! Distributor.Listen(user.id, this)
+ Distributor !? (2000, Distributor.LatestMessages(user.id, 40)) match {
+ case Some(msg: List[Long]) => messages = msg
+ case x =>
+ }
+ }
+ }
+
+ override def localShutdown() {
+ super.localShutdown()
+ for (user <- User.currentUser) {
+ Distributor ! Distributor.Unlisten(user.id, this)
+ }
+ }
+
+ private def lookupMessages(): List[Message] = {
+ val mm = Message.findMessages(messages)
+ messages.flatMap(mm.get)
+ }
+
+ def render = {
+ val messages = lookupMessages()
+ //Sort the tags & words to put the most prominent in the middle
+ <div id="tagcloud">
+ <p id="tag-para">Tags</p>
+ <p>
+ {
+ for ((name, weight) <- Tag.centreWeightedTopNTagFreqs(messages, 20))
+ yield <xml:group><a href={"/tag/" + name}
+ style={"font-size: "+(0.5F + weight)+"em; text-decoration: none;"}>{
+ name}</a> </xml:group>
+ }
+ </p>
+ <p/>
+ <p id="word-para">Words</p>
+ <p>
+ {
+ for ((name, weight) <- Message.centreWeightedTopNWordFreqs(messages, 20))
+ yield <xml:group><a href={"/search/" + name}
+ style={"font-size: "+(0.5F + weight)+"em; text-decoration: none;"}>{
+ name}</a> </xml:group>
+ }
+ </p>
+ </div>
+ }
+
+
+ override def lowPriority = {
+ case UserActor.MessageReceived(msg, _) =>
+ messages = (msg.id.is :: messages).take(40)
+ reRender(false)
+ }
+}
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/Timeline.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/Timeline.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/Timeline.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/Timeline.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,78 @@
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.esme.comet
+
+import net.liftweb.http._
+import net.liftweb.util._
+import net.liftweb.util.Helpers._
+import scala.xml._
+import js._
+import JsCmds._
+import JE._
+
+import org.apache.esme._
+import actor._
+import model._
+import lib._
+
+import java.text._
+
+class Timeline extends CometActor {
+ private var messages: List[Long] = Nil
+
+ override def localSetup() {
+ super.localSetup()
+ for (user <- User.currentUser) {
+ Distributor ! Distributor.Listen(user.id, this)
+ Distributor !? (200, Distributor.LatestMessages(user.id, 40)) match {
+ case Some(msg: List[Long]) => messages = msg
+ case x =>
+ }
+ }
+ }
+
+ override def localShutdown() {
+ super.localShutdown()
+ for (user <- User.currentUser) {
+ Distributor ! Distributor.Unlisten(user.id, this)
+ }
+ }
+
+ def render = {
+ val msgMap = Message.findMessages(messages)
+ val toDisplay = messages.flatMap(msgMap.get)
+ val jsId = "timeline_messages";
+
+ OnLoad(JsCrVar(jsId, JsArray(
+ toDisplay.map(_.asJs) :_*)) &
+ JsFunc("displayMessages", JsVar(jsId), jsId).cmd)
+ }
+
+ override def lowPriority = {
+ case UserActor.MessageReceived(msg, _) =>
+ messages = (msg.id.is :: messages).take(40)
+ reRender(false)
+
+ case Distributor.UserUpdated(_) =>
+ reRender(false)
+ }
+}
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/AtomFeed.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/AtomFeed.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/AtomFeed.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/AtomFeed.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,71 @@
+package org.apache.esme.external
+
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.text._
+import java.util.Locale.US
+
+import org.apache.esme.model.User
+
+object AtomFeed{
+ val dateFormats = List(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", US),
+ new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", US),
+ new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", US),
+ new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", US))
+}
+
+class AtomFeed(user: User, atomURL: String, source: String, truncateChars: Int, tags: List[String])
+ extends Feed(user, atomURL, source, truncateChars, tags) {
+ import scala.xml._
+
+ override def dateFormats = AtomFeed.dateFormats
+
+ override def getEntries(xml: Elem) = xml \ "entry"
+
+ override def getText(node: Node) = {
+ // a title element is mandatory
+ node \ "title" text
+ }
+
+ override def getLink(node: Node) = {
+ // there must be either a link with @rel="alternate"
+ // or a link without @rel or content
+ val link = node \ "link"
+ if (link isEmpty)
+ node \ "content" text
+ else {
+ val alternate = link find(_ \ "@rel" == "alternate")
+ val anyLink = alternate getOrElse((link find(_ \ "@rel" == Nil)).get)
+ anyLink \ "@href" text
+ }
+ }
+
+ override def getDate(node: Node) = {
+ // parseInternetDate(node \ "published" text).getTime
+ val published = node \ "published"
+ val date = if (published isEmpty)
+ node \ "updated"
+ else published
+ parseInternetDate(date text).getTime
+ }
+}
+
Propchange: incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/AtomFeed.scala
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/Feed.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/Feed.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/Feed.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/Feed.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,104 @@
+package org.apache.esme.external
+
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException
+import java.text._
+import java.util.Date
+
+
+import org.apache.commons.httpclient.methods.GetMethod
+
+import org.apache.esme.actor._
+import org.apache.esme.model.User
+
+import org.apache.esme.actor.Distributor.UserCreatedMessage
+
+import net.liftweb.util._
+
+abstract class Feed(val user: User, val url: String, val source: String, val truncateChars: Int, val tags: List[String]) extends UniqueMessageSource {
+ // import java.net.{URLConnection, URL}
+ import scala.xml._
+
+ protected def dateFormats: List[SimpleDateFormat]
+ var dateFormat: SimpleDateFormat = _
+
+ override def apply() = {
+
+ ( for (node <- (getEntries(XML.loadString(responseString))))
+ yield UserCreatedMessage(
+ if (user != null) {user.id} else 0,
+ getText(node) + " " + getLink(node),
+ tags,
+ getDate(node),
+ Empty,
+ source,
+ Empty
+ )
+ ).toList
+ }
+
+ protected def getEntries(xml: Elem): NodeSeq
+
+ protected def getText(xml: Node): String
+
+ protected def getLink(xml: Node): String
+
+ protected def getDate(xml: Node): Long
+
+ protected def responseString() = {
+ // url.openConnection
+ val httpClient = HttpSender.httpClient
+ val method = new GetMethod(url)
+ try {
+ httpClient.executeMethod(method)
+ method.getResponseBodyAsString
+ } catch {
+ case ioe: IOException => ""
+ } finally {
+ method.releaseConnection
+ }
+ }
+
+ protected def parseInternetDate(dateString: String): Date = {
+ val fixedDateString = fixDateString(dateString)
+ if (dateFormat == null) dateFormats.find { df =>
+ try {
+ dateFormat = df
+ return df.parse(fixedDateString)
+ // true
+ } catch {
+ case pe: ParseException => false
+ }
+ }
+ dateFormat.parse(fixedDateString)
+ }
+
+ protected def fixDateString(dateString: String) = {
+ val l = dateString.length
+ if (dateString.charAt(l - 3) == ':')
+ dateString.substring(0,l-3) + dateString.substring(l - 2, l)
+ else dateString
+ }
+
+}
+
Propchange: incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/Feed.scala
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/RssFeed.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/RssFeed.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/RssFeed.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/RssFeed.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,88 @@
+package org.apache.esme.external
+
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.text._
+import java.util.Locale.US
+
+import org.apache.esme.model.User
+import org.apache.esme.model.Message
+import org.apache.esme.actor.Distributor.{UserCreatedMessage=>Msg}
+
+import net.liftweb.util.Empty
+
+object RssFeed {
+ val dateFormats = List(new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", US))
+}
+
+class RssFeed(user: User, rssURL: String, source: String, truncateChars: Int, tags: List[String])
+ extends Feed(user, rssURL, source, truncateChars, tags) {
+ import scala.xml._
+
+ override def dateFormats = RssFeed.dateFormats
+
+ override def getEntries(xml: Elem) = xml \\ "item"
+
+ override def getText(node: Node) = {
+ // if there's no title, get description
+ val title = node \ "title"
+ if (title isEmpty)
+ node \ "description" text
+ else
+ title text
+ }
+
+ override def getLink(node: Node) = {
+ // a link is optional
+ val link = node \ "link"
+ if (link isEmpty)
+ ""
+ else
+ link text
+ }
+
+ override def getDate(node: Node) = {
+ // if there's no published date, take current time
+ val date = node \ "pubDate"
+ if (date isEmpty)
+ System.currentTimeMillis
+ else
+ parseInternetDate(date text).getTime
+ }
+
+ // need to compare by text since a pubDate is not mandatory and indeed, often is missing
+ override def getLastSortedMessages(msgs: List[Msg], lastMessage: Option[Msg]): List[Msg] = {
+ lastMessage match {
+ case Some(message: Msg) =>
+ // a hack to format text identically- difference in urls & trailing whitespace
+ val lastMessageText =
+ Message.create.setTextAndTags(message.text, Nil, Empty).
+ get.getText.trim
+ msgs.takeWhile{ msg =>
+ Message.create.setTextAndTags(msg.text, Nil, Empty).
+ get.getText.trim != lastMessageText
+ }
+ case None => msgs
+ }
+ }.reverse
+}
+
Propchange: incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/RssFeed.scala
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/TwitterFeed.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/TwitterFeed.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/TwitterFeed.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/TwitterFeed.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,61 @@
+package org.apache.esme.external
+
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.esme.actor.UniqueMessageSource
+import org.apache.esme.actor.Distributor.UserCreatedMessage
+import org.apache.esme.model.User
+
+import net.liftweb._
+import util._
+
+object TwitterFeed {
+ val UserTimelineUrl = "http://twitter.com/statuses/user_timeline/"
+ val Protocol = "xml"
+ val Params = ""
+ val tf = new java.text.SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", java.util.Locale.US)
+
+}
+
+class TwitterFeed(val user: User, val twitterUser: String) extends UniqueMessageSource {
+ import java.net.{URLConnection, URL}
+ import scala.xml._
+ import TwitterFeed._
+
+ override def apply() = {
+ val url = new URL(UserTimelineUrl + twitterUser + "." + Protocol + Params)
+ val conn = url.openConnection()
+
+
+ (for (node <- (XML.load(conn.getInputStream) \ "status")) yield UserCreatedMessage(
+ user.id,
+ node \ "text" text,
+ List("twitter"),
+ tf.parse(node \ "created_at" text).getTime,
+ Empty,
+ "twiiter",
+ Empty
+ )).toList
+ }
+
+}
+
Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/MsgFormat.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/MsgFormat.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/MsgFormat.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/MsgFormat.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,117 @@
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.esme.lib
+
+import org.apache.esme._
+import model._
+
+import scala.xml.{NodeSeq, Node, Text}
+
+import net.liftweb._
+import util._
+import http._
+import Helpers._
+
+import java.text.SimpleDateFormat
+
+object IsIE7 extends SessionVar[Boolean]({
+ // "Firefox/3"
+ // "MSIE 7"
+ def findUserAgent(in: List[(String, String)]): Box[String] =
+ in.filter(_._1.equalsIgnoreCase("User-Agent")).map(_._2).firstOption
+
+ val r: Box[Boolean] =
+ for (session <- S.session;
+ agent <- findUserAgent(session.initialHeaders)) yield
+ agent.indexOf("MSIE 7") >= 0
+
+ r openOr false
+})
+
+/*
+trait MsgFormat {
+
+ def formatMsg(in: Message, showReply: Boolean, showConv: Boolean): NodeSeq =
+ formatMsg(in, showReply, showConv, Empty)
+
+ def formatMsg(in: Message, showReply: Boolean, showConv: Boolean,
+ inside: Box[() => NodeSeq]): NodeSeq =
+ <div class="b-list">
+ <table>
+ <tr>
+ <td class="image">{
+ val r: Box[NodeSeq] =
+ for (user <- in.author.obj;
+ image <- user.image if !IsIE7.is) yield <img src={image} alt={user.niceName}/>
+
+ r openOr <xml:group> </xml:group>
+ }</td>
+ <td class="message">
+ <div class="outer">
+ <div class="inner clear">
+ <p class="text">{in.digestedXHTML}</p>
+ </div>
+ <p class="date">{in.author.obj.map(u => <a href={"/user/"+urlEncode(u.nickname)}>{u.niceName}</a>).openOr("")}:
+ { dateFormat(in.when)} {
+ if (showReply) <a href={"javascript:setReplyTo("+in.id+","+
+ in.getText.encJs+
+ ")"}>reply</a>
+ else Text("")
+
+ }
+ {
+ in.conversation.can.toList.map(cid => <a href={"/conversation/"+cid}>conversation</a>)
+ }
+ </p>
+ </div>
+ </td>
+ <td class="tag">
+ {
+ in.tags.map(tag => <p><a href={"/tag/"+urlEncode(tag)}>{tag}</a></p>)
+ }
+ </td>
+ </tr>
+ </table>
+ {
+ inside.map(_()).openOr(Text(""))
+ }
+ </div>
+
+ private def dateFormat(in: Long): String = {
+ val delta = ((millis - in) / 1000L).toInt
+
+ delta match {
+ case 1 => "1 Second ago"
+ case n if n < 60 => ""+n+" Seconds ago"
+ case n if n >= 60 && n < 120 => "1 Minute ago"
+ case n if n < 3600 => ""+(n / 60)+" Minutes ago"
+ case n =>
+ (n / 3600) match {
+ case 1 => "1 Hour ago"
+ case n if n < 24 => ""+n+" Hours ago"
+ case _ => val df = new SimpleDateFormat("MM/dd HH:mm zzz")
+ dateFormatter.format(new java.util.Date(in))
+ }
+ }
+ }
+}
+*/
Re: svn commit: r736864 [1/3] - in /incubator/esme/trunk/server: ./ src/main/scala/bootstrap/liftweb/ src/main/scala/org/ src/main/scala/org/apache/ src/main/scala/org/apache/esme/ src/main/scala/org/apache/esme/actor/ src/main/scala/org/apache/esme/api/ s...
Posted by Daniel Kulp <dk...@apache.org>.
> ===========================================================================
>=== --- incubator/esme/trunk/server/pom.xml (original)
> +++ incubator/esme/trunk/server/pom.xml Thu Jan 22 16:26:46 2009
> @@ -1,7 +1,7 @@
> <?xml version="1.0" encoding="UTF-8"?>
> <project xmlns="http://maven.apache.org/POM/4.0.0"
> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
> http://maven.apache.org/maven-v4_0_0.xsd">
> <modelVersion>4.0.0</modelVersion>
> - <groupId>us.esme</groupId>
> + <groupId>org.apache</groupId>
> <artifactId>esme</artifactId>
> <version>0.2.3-SNAPSHOT</version>
> <packaging>war</packaging>
The group id should be org.apache.esme to make all artifacts (when you get to
a point where you have multiple artifacts) go into sub dirs of the esme
stuff.
--
Daniel Kulp
dkulp@apache.org
http://dankulp.com/blog