Skip to content
Browse files

adds first cut of support for activitystrea.ms protocol and associate…

…d REST endpoint
  • Loading branch information...
1 parent ac70c24 commit 7e14c53e391d85f21e28ccc684b5ecd3d6bae542 @mindcrime mindcrime committed Jan 18, 2012
View
80 .classpath
@@ -8,6 +8,7 @@
<classpathentry kind="src" path="grails-app/ldap"/>
<classpathentry kind="src" path="test/unit"/>
<classpathentry kind="src" path="test/integration"/>
+ <classpathentry kind="src" path="src/groovy"/>
<classpathentry kind="lib" path="lib/activation.jar"/>
<classpathentry kind="lib" path="lib/commons-math-2.1.jar"/>
<classpathentry kind="lib" path="lib/lucene-core-3.0.1.jar"/>
@@ -17,7 +18,6 @@
<classpathentry kind="lib" path="lib/commons-lang-2.5.jar"/>
<classpathentry kind="lib" path="lib/commons-pool-1.5.4.jar"/>
<classpathentry kind="lib" path="lib/ldapbp-1.0.jar"/>
- <classpathentry kind="lib" path="lib/log4j-1.2.9.jar"/>
<classpathentry kind="lib" path="lib/org.springframework.asm-3.0.5.RELEASE.jar"/>
<classpathentry kind="lib" path="lib/org.springframework.expression-3.0.5.RELEASE.jar"/>
<classpathentry kind="lib" path="lib/spring-beans-3.0.5.RELEASE.jar"/>
@@ -85,6 +85,26 @@
<attribute name="com.springsource.sts.grails.core.SOURCE_FOLDER" value="true"/>
</attributes>
</classpathentry>
+ <classpathentry kind="src" path=".link_to_grails_plugins/jms-1.2/grails-app/utils">
+ <attributes>
+ <attribute name="com.springsource.sts.grails.core.SOURCE_FOLDER" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path=".link_to_grails_plugins/jms-1.2/grails-app/services">
+ <attributes>
+ <attribute name="com.springsource.sts.grails.core.SOURCE_FOLDER" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path=".link_to_grails_plugins/jms-1.2/src/java">
+ <attributes>
+ <attribute name="com.springsource.sts.grails.core.SOURCE_FOLDER" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path=".link_to_grails_plugins/jms-1.2/src/groovy">
+ <attributes>
+ <attribute name="com.springsource.sts.grails.core.SOURCE_FOLDER" value="true"/>
+ </attributes>
+ </classpathentry>
<classpathentry kind="src" path=".link_to_grails_plugins/code-coverage-1.2/grails-app/i18n">
<attributes>
<attribute name="com.springsource.sts.grails.core.SOURCE_FOLDER" value="true"/>
@@ -120,5 +140,63 @@
<attribute name="com.springsource.sts.grails.core.SOURCE_FOLDER" value="true"/>
</attributes>
</classpathentry>
+ <classpathentry kind="lib" path="/usr/java/junit3.8.1/junit.jar"/>
+ <classpathentry kind="lib" path="lib/arq-2.8.7.jar"/>
+ <classpathentry kind="lib" path="lib/concurrent.jar"/>
+ <classpathentry kind="lib" path="lib/iri-0.8.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-aop-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-appclient.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-aspect-jdk50-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-common-core.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-deployers-client-spi.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-deployers-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-deployers-core-spi.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-deployers-core.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-deployment.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-ejb3-common-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-ejb3-core-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-ejb3-ext-api.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-ejb3-proxy-clustered-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-ejb3-proxy-impl-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-ejb3-proxy-spi-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-ejb3-security-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-ha-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-ha-legacy-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-iiop-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-integration.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-j2se.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-javaee.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-jsr77-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-logging-jdk.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-logging-log4j.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-logging-spi.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-main-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-mdr.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-messaging-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-remoting.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-security-spi.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-serialization.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-srp-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-system-client.jar"/>
+ <classpathentry kind="lib" path="lib/jboss-system-jmx-client.jar"/>
+ <classpathentry kind="lib" path="lib/jbosscx-client.jar"/>
+ <classpathentry kind="lib" path="lib/jbossjts-integration.jar"/>
+ <classpathentry kind="lib" path="lib/jbossjts.jar"/>
+ <classpathentry kind="lib" path="lib/jbosssx-as-client.jar"/>
+ <classpathentry kind="lib" path="lib/jbosssx-client.jar"/>
+ <classpathentry kind="lib" path="lib/jena-2.6.4.jar"/>
+ <classpathentry kind="lib" path="lib/jmx-client.jar"/>
+ <classpathentry kind="lib" path="lib/jmx-invoker-adaptor-client.jar"/>
+ <classpathentry kind="lib" path="lib/jnp-client.jar"/>
+ <classpathentry kind="lib" path="lib/log4j-1.2.16.jar"/>
+ <classpathentry kind="lib" path="lib/sample.jar"/>
+ <classpathentry kind="lib" path="lib/shiro-core-1.1.0.jar"/>
+ <classpathentry kind="lib" path="lib/shiro-web-1.1.0.jar"/>
+ <classpathentry kind="lib" path="lib/stax-api-1.0.1.jar"/>
+ <classpathentry kind="lib" path="lib/trove.jar"/>
+ <classpathentry kind="lib" path="lib/wstx-asl-3.2.9.jar"/>
+ <classpathentry kind="lib" path="lib/xercesImpl-2.9.1.jar"/>
+ <classpathentry kind="lib" path="lib/jackson-all-1.9.3.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
View
2 .project
@@ -32,7 +32,7 @@
<link>
<name>.link_to_grails_plugins</name>
<type>2</type>
- <locationURI>GRAILS_ROOT/1.3.6/projects/quoddy2/plugins</locationURI>
+ <locationURI>GRAILS_ROOT/1.3.7.BUILD-SNAPSHOT/projects/quoddy2/plugins</locationURI>
</link>
</linkedResources>
</projectDescription>
View
5 .settings/org.eclipse.wst.common.component
@@ -5,5 +5,10 @@
<wb-resource deploy-path="/" source-path="/grails-app/listeners"/>
<wb-resource deploy-path="/" source-path="/test/unit"/>
<wb-resource deploy-path="/" source-path="/test/integration"/>
+ <wb-resource deploy-path="/" source-path="/.link_to_grails_plugins/jms-1.2/grails-app/utils"/>
+ <wb-resource deploy-path="/" source-path="/.link_to_grails_plugins/jms-1.2/grails-app/services"/>
+ <wb-resource deploy-path="/" source-path="/.link_to_grails_plugins/jms-1.2/src/java"/>
+ <wb-resource deploy-path="/" source-path="/.link_to_grails_plugins/jms-1.2/src/groovy"/>
+ <wb-resource deploy-path="/" source-path="/src/groovy"/>
</wb-module>
</project-modules>
View
4 grails-app/conf/Config.groovy
@@ -50,15 +50,15 @@ grails.project.groupId = appName // change this to alter the default package nam
grails.mime.file.extensions = true // enables the parsing of file extensions from URLs into the request format
grails.mime.use.accept.header = false
grails.mime.types = [ html: ['text/html','application/xhtml+xml'],
- xml: ['text/xml', 'application/xml'],
+ // xml: ['text/xml', 'application/xml'],
text: 'text/plain',
js: 'text/javascript',
rss: 'application/rss+xml',
atom: 'application/atom+xml',
css: 'text/css',
csv: 'text/csv',
all: '*/*',
- json: ['application/json','text/json'],
+ // json: ['application/json','text/json'],
form: 'application/x-www-form-urlencoded',
multipartForm: 'multipart/form-data'
]
View
4 grails-app/conf/DataSource.groovy
@@ -5,8 +5,8 @@ dataSource {
driverClassName = "org.postgresql.Driver"
// username = "sa"
username = "postgres"
- // password = ""
password = ""
+ // password = ""
logSql=false
}
hibernate {
@@ -20,6 +20,8 @@ environments {
dataSource {
dbCreate = "update" // one of 'create', 'create-drop','update'
url = "jdbc:postgresql:quoddy2";
+ // dbCreate = "create-drop"
+ // url = "jdbc:hsqldb:mem:devDb
}
}
test {
View
71 grails-app/controllers/org/fogbeam/quoddy/ActivityStreamController.groovy
@@ -1,9 +1,13 @@
package org.fogbeam.quoddy
+import org.codehaus.jackson.map.ObjectMapper
+import org.fogbeam.quoddy.integration.activitystream.ActivityStreamEntry
+
class ActivityStreamController
{
def activityStreamService;
+ def activityStreamTransformerService;
def userService;
def jmsService;
def eventQueueService;
@@ -72,6 +76,7 @@ class ActivityStreamController
else
{
println "logged in; so proceeding...";
+ // def originTime = new Date().getTime();
// get our user
user = userService.findUserByUserId( session.user.userId );
@@ -105,16 +110,20 @@ class ActivityStreamController
session.user = user;
- def originTime = new Date().getTime();
- Activity activity = new Activity(text:newStatus.text);
- activity.creator = user;
- activity.originTime = originTime;
+
+ Activity activity = new Activity(content:newStatus.text);
+ activity.title = "Internal Activity";
+ activity.url = new URL( "http://www.example.com" );
+ activity.verb = "status_update";
+ activity.userActor = user;
+ activity.published = new Date(); // set published to "now"
activityStreamService.saveActivity( activity );
Map msg = new HashMap();
- msg.creator = activity.creator.userId;
+ msg.creator = activity.userActor.userId;
msg.text = newStatus.text;
- msg.originTime = originTime;
+ // msg.published = activity.published;
+ msg.originTime = activity.dateCreated.time;
println "sending message to JMS";
jmsService.send( queue: 'uitestActivityQueue', msg, 'standard', null );
@@ -124,5 +133,51 @@ class ActivityStreamController
println( "returning status 200" );
render( "OK");
- }
-}
+ }
+
+ def index = {
+
+ switch(request.method){
+ case "POST":
+ // def originTime = new Date().getTime();
+ println "Create\n"
+ // String json = request.reader.text;
+ String json = params.activityJson;
+ println("Got json:\n " + json );
+
+ ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally
+
+ // convert from JSON to Groovy classes
+ ActivityStreamEntry streamEntry = mapper.readValue(json, ActivityStreamEntry.class);
+
+ // map to our internal representation and save / msg
+ Activity activity = activityStreamTransformerService.getActivity( streamEntry );
+ activityStreamService.saveActivity( activity );
+
+ // send notification message
+ Map msg = new HashMap();
+ msg.creator = activity.userActor.userId;
+ msg.text = activity.content;
+ // msg.published = activity.published;
+ msg.originTime = activity.dateCreated.time;
+
+ println "sending message to JMS";
+ jmsService.send( queue: 'uitestActivityQueue', msg, 'standard', null );
+
+ // println streamEntry.toString();
+
+ break
+ case "GET":
+ println "Retrieve\n"
+ break
+ case "PUT":
+ println "Update\n"
+ break
+ case "DELETE":
+ println "Delete\n"
+ break
+ }
+
+ render "OK";
+ }
+}
View
9 grails-app/controllers/org/fogbeam/quoddy/SpecialController.groovy
@@ -0,0 +1,9 @@
+package org.fogbeam.quoddy
+
+class SpecialController
+{
+ def post_as = {
+
+ [];
+ }
+}
View
12 grails-app/controllers/org/fogbeam/quoddy/StatusController.groovy
@@ -1,4 +1,4 @@
-package org.fogbeam.quoddy
+package org.fogbeam.quoddy;
import org.fogbeam.quoddy.Activity;
import org.fogbeam.quoddy.StatusUpdate;
@@ -14,11 +14,11 @@ class StatusController {
User user = null;
- if( !session.user )
+ if( !session.user )
{
flash.message = "Must be logged in before updating status";
}
- else
+ else
{
println "logged in; so proceeding...";
@@ -49,7 +49,7 @@ class StatusController {
}
else
{
- // handle failure to update User
+ // handle failure to update User
}
session.user = user;
@@ -76,7 +76,7 @@ class StatusController {
redirect( controller:"home", action:"index", params:[userId:user.userId]);
}
- def listUpdates =
+ def listUpdates =
{
User user = null;
List<StatusUpdate> updates = new ArrayList<StatusUpdate>();
@@ -96,6 +96,6 @@ class StatusController {
}
[updates:updates]
- }
+ }
}
View
238 grails-app/domain/org/fogbeam/quoddy/Activity.groovy
@@ -1,9 +1,239 @@
package org.fogbeam.quoddy
+/* TODO: map the fields of this class to the activitystrea.ms protocol */
+/* SPEC: http://activitystrea.ms/specs/json/1.0/ */
+/*
+ In its simplest form, an activity consists of an actor, a verb, an an object,
+ and a target. It tells the story of a person performing an action on or with
+ an object -- "Geraldine posted a photo to her album" or "John shared a video".
+ In most cases these components will be explicit, but they may also be implied.
+
+ It is a goal of this specification to provide sufficient metadata about an activity
+ such that a consumer of the data can present it to a user in a rich human-friendly
+ format. This may include constructing readable sentences about the activity that
+ occurred, visual representations of the activity, or combining similar activities
+ for display.
+*/
+
+
+/*
+
+
+Following is a simple, minimal example of a JSON serialized activity:
+
+ {
+ "published": "2011-02-10T15:04:55Z",
+ "actor": {
+ "url": "http://example.org/martin",
+ "objectType" : "person",
+ "id": "tag:example.org,2011:martin",
+ "image": {
+ "url": "http://example.org/martin/image",
+ "width": 250,
+ "height": 250
+ },
+ "displayName": "Martin Smith"
+ },
+ "verb": "post",
+ "object" : {
+ "url": "http://example.org/blog/2011/02/entry",
+ "id": "tag:example.org,2011:abc123/xyz"
+ },
+ "target" : {
+ "url": "http://example.org/blog/",
+ "objectType": "blog",
+ "id": "tag:example.org,2011:abc123",
+ "displayName": "Martin's Blog"
+ }
+ }
+
+*/
+
+
+/*
+
+ {
+ "items" : [
+ {
+ "published": "2011-02-10T15:04:55Z",
+ "foo": "some extension property",
+ "generator": {
+ "url": "http://example.org/activities-app"
+ },
+ "provider": {
+ "url": "http://example.org/activity-stream"
+ },
+ "title": "Martin posted a new video to his album.",
+ "actor": {
+ "url": "http://example.org/martin",
+ "objectType": "person",
+ "id": "tag:example.org,2011:martin",
+ "foo2": "some other extension property",
+ "image": {
+ "url": "http://example.org/martin/image",
+ "width": 250,
+ "height": 250
+ },
+ "displayName": "Martin Smith"
+ },
+ "verb": "post",
+ "object" : {
+ "url": "http://example.org/album/my_fluffy_cat.jpg",
+ "objectType": "photo",
+ "id": "tag:example.org,2011:my_fluffy_cat",
+ "image": {
+ "url": "http://example.org/album/my_fluffy_cat_thumb.jpg",
+ "width": 250,
+ "height": 250
+ }
+ },
+ "target": {
+ "url": "http://example.org/album/",
+ "objectType": "photo-album",
+ "id": "tag:example.org,2011:abc123",
+ "displayName": "Martin's Photo Album",
+ "image": {
+ "url": "http://example.org/album/thumbnail.jpg",
+ "width": 250,
+ "height": 250
+ }
+ }
+ }
+ ]
+ }
+*/
+
+
+/*
+
+ 3.2. Activity Serialization
+
+ Property Value Description
+
+ actor Object Describes the entity that performed the activity. An activity MUST contain one actor property whose value is a single Object.
+ content JSON [RFC4627] String Natural-language description of the activity encoded as a single JSON String containing HTML markup. Visual elements such as thumbnail images MAY be included. An activity MAY contain a content property.
+ generator Object Describes the application that generated the activity. An activity MAY contain a generator property whose value is a single Object.
+ icon Media Link Description of a resource providing a visual representation of the object, intended for human consumption. The image SHOULD have an aspect ratio of one (horizontal) to one (vertical) and SHOULD be suitable for presentation at a small size. An activity MAY have an icon property.
+ id JSON [RFC4627] String Provides a permanent, universally unique identifier for the activity in the form of an absolute IRI [RFC3987]. An activity SHOULD contain a single id property. If an activity does not contain an id property, consumers MAY use the value of the url property as a less-reliable, non-unique identifier.
+ object Object Describes the primary object of the activity. For instance, in the activity, "John saved a movie to his wishlist", the object of the activity is "movie". An activity SHOULD contain an object property whose value is a single Object. If the object property is not contained, the primary object of the activity MAY be implied by context.
+ published [RFC3339] date-time The date and time at which the activity was published. An activity MUST contain a published property.
+ provider Object Describes the application that published the activity. Note that this is not necessarily the same entity that generated the activity. An activity MAY contain a provider property whose value is a single Object.
+ target Object Describes the target of the activity. The precise meaning of the activity's target is dependent on the activities verb, but will often be the object the English preposition "to". For instance, in the activity, "John saved a movie to his wishlist", the target of the activity is "wishlist". The activity target MUST NOT be used to identity an indirect object that is not a target of the activity. An activity MAY contain a target property whose value is a single Object.
+ title JSON [RFC4627] String Natural-language title or headline for the activity encoded as a single JSON String containing HTML markup. An activity MAY contain a title property.
+ updated [RFC3339] date-time The date and time at which a previously published activity has been modified. An Activity MAY contain an updated property.
+ url JSON [RFC4627] String An IRI [RFC3987] identifying a resource providing an HTML representation of the activity. An activity MAY contain a url property.
+ verb JSON [RFC4627] String Identifies the action that the activity describes. An activity SHOULD contain a verb property whose value is a JSON String that is non-empty and matches either the "isegment-nz-nc" or the "IRI" production in [RFC3339]. Note that the use of a relative reference other than a simple name is not allowed. If the verb is not specified, or if the value is null, the verb is assumed to be "post".
+
+*/
+
+/*
+
+ 3.4. Object Serialization
+
+ Property Value Description
+
+ attachments JSON [RFC4627] Array of Objects A collection of one or more additional, associated objects, similar to the concept of attached files in an email message. An object MAY have an attachments property whose value is a JSON Array of Objects.
+ author Object Describes the entity that created or authored the object. An object MAY contain a single author property whose value is an Object of any type. Note that the author field identifies the entity that created the object and does not necessarily identify the entity that published the object. For instance, it may be the case that an object created by one person is posted and published to a system by an entirely different entity.
+ content JSON [RFC4627] String Natural-language description of the object encoded as a single JSON String containing HTML markup. Visual elements such as thumbnail images MAY be included. An object MAY contain a content property.
+ displayName JSON [RFC4627] String A natural-language, human-readable and plain-text name for the object. HTML markup MUST NOT be included. An object MAY contain a displayName property. If the object does not specify an objectType property, the object SHOULD specify a displayName.
+ downstreamDuplicates JSON [RFC4627] Array of Strings A JSON Array of one or more absolute IRI's [RFC3987] identifying objects that duplicate this object's content. An object SHOULD contain a downstreamDuplicates property when there are known objects, possibly in a different system, that duplicate the content in this object. This MAY be used as a hint for consumers to use when resolving duplicates between objects received from different sources.
+ id JSON [RFC4627] String Provides a permanent, universally unique identifier for the object in the form of an absolute IRI [RFC3987]. An object SHOULD contain a single id property. If an object does not contain an id property, consumers MAY use the value of the url property as a less-reliable, non-unique identifier.
+ image Media Link Description of a resource providing a visual representation of the object, intended for human consumption. An object MAY contain an image property whose value is a Media Link.
+ objectType JSON [RFC4627] String Identifies the type of object. An object MAY contain an objectType property whose value is a JSON String that is non-empty and matches either the "isegment-nz-nc" or the "IRI" production in [RFC3987]. Note that the use of a relative reference other than a simple name is not allowed. If no objectType property is contained, the object has no specific type.
+ published [RFC3339] date-time The date and time at which the object was published. An object MAY contain a published property.
+ summary JSON [RFC4627] String Natural-language summarization of the object encoded as a single JSON String containing HTML markup. Visual elements such as thumbnail images MAY be included. An activity MAY contain a summary property.
+ updated [RFC3339] date-time The date and time at which a previously published object has been modified. An Object MAY contain an updated property.
+ upstreamDuplicates JSON [RFC4627] Array of Strings A JSON Array of one or more absolute IRI's [RFC3987] identifying objects that duplicate this object's content. An object SHOULD contain an upstreamDuplicates property when a publisher is knowingly duplicating with a new ID the content from another object. This MAY be used as a hint for consumers to use when resolving duplicates between objects received from different sources.
+ url JSON [RFC4627] String An IRI [RFC3987] identifying a resource providing an HTML representation of the object. An object MAY contain a url property
+
+*/
class Activity {
+
+ public Activity()
+ {
+ this.uuid = java.util.UUID.randomUUID().toString();
+ }
- String text;
- User creator;
- Long originTime;
- Date dateCreated;
+ static constraints = {
+
+ updated(nullable:true);
+ icon(nullable:true);
+
+ userActor(nullable:true);
+
+ actorUuid(nullable:true);
+ actorUrl(nullable:true);
+ actorContent(nullable:true);
+ actorDisplayName(nullable:true);
+ actorObjectType(nullable:true);
+ actorImageUrl(nullable:true);
+ actorImageHeight(nullable:true);
+ actorImageWidth(nullable:true);
+
+ objectUuid(nullable:true);
+ objectUrl(nullable:true);
+ objectContent(nullable:true);
+ objectDisplayName(nullable:true);
+ objectObjectType(nullable:true);
+ objectImageUrl(nullable:true);
+ objectImageHeight(nullable:true);
+ objectImageWidth(nullable:true);
+
+ targetUuid(nullable:true);
+ targetUrl(nullable:true);
+ targetContent(nullable:true);
+ targetDisplayName(nullable:true);
+ targetObjectType(nullable:true);
+ targetImageUrl(nullable:true);
+ targetImageHeight(nullable:true);
+ targetImageWidth(nullable:true);
+
+ generatorUrl(nullable:true);
+ providerUrl(nullable:true);
+
+ dateCreated()
+ }
+
+ String content;
+ Date published;
+ String title;
+ Date updated;
+ URL url;
+ String verb;
+ URL icon;
+ String uuid;
+
+ User userActor;
+
+ String actorUuid;
+ String actorUrl;
+ String actorContent;
+ String actorDisplayName;
+ String actorObjectType;
+ String actorImageUrl;
+ String actorImageHeight;
+ String actorImageWidth;
+
+ String objectUuid;
+ String objectUrl;
+ String objectContent;
+ String objectDisplayName;
+ String objectObjectType;
+ String objectImageUrl;
+ String objectImageHeight;
+ String objectImageWidth;
+
+ String targetUuid;
+ String targetUrl;
+ String targetContent;
+ String targetDisplayName;
+ String targetObjectType;
+ String targetImageUrl;
+ String targetImageHeight;
+ String targetImageWidth;
+
+ String generatorUrl
+ String providerUrl;
+
+ Date dateCreated;
}
View
31 grails-app/services/org/fogbeam/quoddy/ActivityStreamService.groovy
@@ -39,11 +39,16 @@ class ActivityStreamService {
public void saveActivity( Activity activity )
{
println "about to save activity...";
- if( !activity.save() )
+ if( !activity.save(flush:true) )
{
println( "Saving activity FAILED");
activity.errors.allErrors.each { println it };
}
+ else
+ {
+ println "Successfully saved Activity: ${activity.id}";
+ }
+
}
@@ -107,7 +112,8 @@ class ActivityStreamService {
println "Messages to read from queue: ${msgsToRead}";
- long oldestOriginTime = Long.MAX_VALUE;
+ // long oldestOriginTime = Long.MAX_VALUE;
+ long oldestOriginTime = new Date().getTime();
// NOTE: we could avoid iterating over this list again by returning the "oldest message time"
// as part of this call. But it'll mean wrapping this stuff up into an object of some
@@ -123,6 +129,7 @@ class ActivityStreamService {
}
println "oldestOriginTime: ${oldestOriginTime}";
+ println "as date: " + new Date( oldestOriginTime);
// convert our messages to Activity instances and
// put them in this list...
@@ -139,9 +146,9 @@ class ActivityStreamService {
Activity activity = new Activity();
// println "msg class: " + msg?.getClass().getName();
- activity.creator = userService.findUserByUserId( msg.creator );
- activity.text = msg.text;
- activity.originTime = msg.originTime;
+ activity.userActor = userService.findUserByUserId( msg.creator );
+ activity.content = msg.text;
+ activity.dateCreated = new Date( msg.originTime );
recentActivities.add( activity );
}
@@ -170,9 +177,10 @@ class ActivityStreamService {
Date cutoffDate = cal.getTime();
println "Using ${cutoffDate} as cutoffDate";
+ println "Using ${new Date(oldestOriginTime)} as oldestOriginTime";
List<User> friends = userService.listFriends( user );
- if( friends != null && friends.size() > 0 )
+ if( friends != null && friends.size() >= 0 )
{
println "Found ${friends.size()} friends";
List<Integer> friendIds = new ArrayList<Integer>();
@@ -183,10 +191,15 @@ class ActivityStreamService {
friendIds.add( id );
}
-
+
+ // for the purpose of this query, treat a user as their own friend... that is, we
+ // will want to read Activities created by this user (we see out own updates in our
+ // own feed)
+ friendIds.add( user.id );
+
List<Activity> queryResults =
- Activity.executeQuery( "select activity from Activity as activity where activity.dateCreated >= :cutoffDate and activity.creator.id in (:friendIds) and activity.originTime < :oldestOriginTime order by activity.dateCreated desc",
- ['cutoffDate':cutoffDate, 'oldestOriginTime':oldestOriginTime, 'friendIds':friendIds], ['max': recordsToRetrieve ]);
+ Activity.executeQuery( "select activity from Activity as activity where activity.dateCreated >= :cutoffDate and activity.userActor.id in (:friendIds) and activity.dateCreated < :oldestOriginTime order by activity.dateCreated desc",
+ ['cutoffDate':cutoffDate, 'oldestOriginTime':new Date(oldestOriginTime), 'friendIds':friendIds], ['max': recordsToRetrieve ]);
println "adding ${queryResults.size()} activities read from DB";
recentActivities.addAll( queryResults );
View
75 grails-app/services/org/fogbeam/quoddy/EventQueueService.groovy
@@ -17,10 +17,6 @@ class EventQueueService
// now, figure out which user(s) are interested in this message, and put it on
// all the appropriate queues
-
-
- // NOTE: in this prototype version, we'll assume anybody who is logged in
- // is interested in messages from every other user (note: except themselves)
Set<Map.Entry<String, Deque<Map>>> entries = eventQueues.entrySet();
println "got entrySet from eventQueues object: ${entries}";
for( Map.Entry<String, Deque<Map>> entry : entries )
@@ -30,50 +26,51 @@ class EventQueueService
String key = entry.getKey();
- // don't put the message on the queue for the user who sent it
- if( !key.equalsIgnoreCase( msg.creator ))
+
+ // TODO: don't offer message unless the owner of this queue
+ // and the event creator, are friends (or the owner *is* the creator)
+ println "msg creator: ${msg.creator}";
+ User msgCreator = userService.findUserByUserId( msg.creator );
+ if( msgCreator )
+ {
+ println "found User object for ${msgCreator.userId}";
+ }
+
+ FriendCollection friendCollection = FriendCollection.findByOwnerUuid( msgCreator.uuid );
+ if( friendCollection )
{
+ println "got a valid friends collection for ${msgCreator.userId}";
+ }
- // TODO: don't offer message unless the owner of this queue
- // and the event creator, are friends.
- println "msg creator: ${msg.creator}";
- User msgCreator = userService.findUserByUserId( msg.creator );
- if( msgCreator )
- {
- println "found User object for ${msgCreator.userId}";
- }
-
- FriendCollection friendCollection = FriendCollection.findByOwnerUuid( msgCreator.uuid );
- if( friendCollection )
+ Set<String> friends = friendCollection.friends;
+ if( friends )
+ {
+ println "got valid friends set: ${friends}";
+ for( String friend : friends )
{
- println "got a valid friends collection for ${msgCreator.userId}";
+ println "friend: ${friend}";
}
-
- Set<String> friends = friendCollection.friends;
- if( friends )
+ }
+ User targetUser = userService.findUserByUserId( key );
+ if( friends.contains( targetUser.uuid ) || msgCreator.uuid.equals( targetUser.uuid ) )
+ {
+ println "match found, offering message";
+ Deque<Map> userQueue = entry.getValue();
+ if( msg instanceof Map )
{
- println "got valid friends set: ${friends}";
- for( String friend : friends )
- {
- println "friend: ${friend}";
- }
+ println "MapMessage being offered";
+ userQueue.offerFirst( msg );
}
- User targetUser = userService.findUserByUserId( key );
- if( friends.contains( targetUser.uuid ))
+ else
{
- println "match found, offering message";
- Deque<Map> userQueue = entry.getValue();
- if( msg instanceof Map )
- {
- println "MapMessage being offered";
- userQueue.offerFirst( msg );
- }
- else
- {
- println "WTF is this? ${msg}";
- }
+ println "WTF is this? ${msg}";
}
}
+
+
+
+
+
}
println "done processing eventQueue instances";
}
View
56 grails-app/services/org/fogbeam/quoddy/transformer/ActivityStreamTransformerService.groovy
@@ -0,0 +1,56 @@
+package org.fogbeam.quoddy.transformer
+
+import java.net.URL
+import java.text.SimpleDateFormat
+
+import org.fogbeam.quoddy.Activity
+import org.fogbeam.quoddy.User
+import org.fogbeam.quoddy.integration.activitystream.ActivityStreamEntry
+
+class ActivityStreamTransformerService
+{
+ // see note here: http://stackoverflow.com/questions/2580925/simpledateformat-parsing-date-with-z-literal
+ // about parsing the RFC3339 format with the 'Z' literal
+ // "yyyy-MM-dd'T'HH:mm:ss.SSSZ" -- with fractional seconds part (do we need this?)
+ private static final String RFC3339_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
+ def userService;
+
+ public Activity getActivity( ActivityStreamEntry entry )
+ {
+ Activity activity = new Activity();
+
+ activity.content = entry.content;
+ activity.title = entry.title;
+ if( entry.url )
+ {
+ activity.url = new URL(entry.url);
+ }
+
+ // do date translation
+ SimpleDateFormat sdf = new SimpleDateFormat( RFC3339_DATE_FORMAT );
+ String eventPublishedDate = entry.published;
+ if( eventPublishedDate.endsWith( "Z" ))
+ {
+ eventPublishedDate = eventPublishedDate.replaceAll( "Z\$", "GMT+00:00" );
+ }
+
+ activity.published = sdf.parse( eventPublishedDate );
+
+ activity.verb = entry.verb;
+
+ // do user translation
+ println "Looking for user ${entry.actor.id}";
+ User userActor = userService.findUserByUserId(entry.actor.id);
+ if( userActor )
+ {
+ println "Found user: ${userActor}";
+ activity.userActor = userActor;
+ }
+ else
+ {
+ println "failed to lookup Actor as a User in our system.";
+ }
+
+ return activity;
+ }
+}
View
8 grails-app/views/_activityStream.gsp
@@ -3,18 +3,18 @@
<div class="aseWrapper">
<div class="aseAvatarBlock">
- <img src="${createLink(controller:'profilePic',action:'thumbnail',id:activity.creator.userId)}" />
+ <img src="${createLink(controller:'profilePic',action:'thumbnail',id:activity.userActor.userId)}" />
</div>
<div class="aseTitleBar"> <!-- http://localhost:8080/quoddy/user/viewUser?userId=testuser2 -->
- <a href="${createLink(controller:'user', action:'viewUser', params:[userId:activity.creator.userId])}">${activity.creator.fullName}</a>
+ <a href="${createLink(controller:'user', action:'viewUser', params:[userId:activity.userActor.userId])}">${activity.userActor.fullName}</a>
</div>
<div class="activityStreamEntry">
- ${activity.text}
+ ${activity.content}
</div>
<div class="aseClear" >
</div>
<div class="aseFooter" >
- ${activity.dateCreated}
+ <g:formatDate date="${activity.dateCreated}" type="datetime" style="LONG" timeStyle="SHORT"/>
</div>
</div>
View
18 grails-app/views/special/post_as.gsp
@@ -0,0 +1,18 @@
+<html>
+
+ <head>
+ <title>Quoddy: Post ActivityStream as JSON</title>
+ <meta name="layout" content="main" />
+ <nav:resources />
+ </head>
+
+ <body>
+ <p />
+ <g:form controller="activityStream" action="index" method="POST">
+ <g:textArea name="activityJson" style="height:400px;width:600px;" />
+ <br />
+ <input type="submit" name="submitJson" value="SubmitJson" />
+ </g:form>
+ </body>
+
+</html>
View
BIN lib/arq-2.8.7.jar
Binary file not shown.
View
BIN lib/iri-0.8.jar
Binary file not shown.
View
BIN lib/jackson-all-1.9.3.jar
Binary file not shown.
View
BIN lib/jena-2.6.4.jar
Binary file not shown.
View
BIN lib/stax-api-1.0.1.jar
Binary file not shown.
View
BIN lib/wstx-asl-3.2.9.jar
Binary file not shown.
View
0 src/groovy/groovy.txt
No changes.
View
20 src/groovy/org/fogbeam/quoddy/integration/activitystream/ActivityStreamEntry.groovy
@@ -0,0 +1,20 @@
+package org.fogbeam.quoddy.integration.activitystream
+
+class ActivityStreamEntry
+{
+ String content;
+ String published;
+ String title;
+ String url;
+ String verb;
+
+ Actor actor;
+ org.fogbeam.quoddy.integration.activitystream.Object object;
+ Target target;
+
+
+ public String toString()
+ {
+ return "ActivityStreamEntry: published: ${published}, verb: ${verb}, actor: ${actor}, object: ${object}, target: ${target}";
+ }
+}
View
18 src/groovy/org/fogbeam/quoddy/integration/activitystream/Actor.groovy
@@ -0,0 +1,18 @@
+package org.fogbeam.quoddy.integration.activitystream
+
+class Actor {
+
+ static constraints = {
+ }
+
+ String displayName;
+ String objectType;
+ String id;
+ String url;
+ Image image;
+
+ public String toString()
+ {
+ return "Actor: displayName: ${displayName}, objectType: ${objectType}, id: ${id}, url: ${url}, image: ${image}";
+ }
+}
View
13 src/groovy/org/fogbeam/quoddy/integration/activitystream/Image.groovy
@@ -0,0 +1,13 @@
+package org.fogbeam.quoddy.integration.activitystream
+
+class Image
+{
+ String url;
+ String width;
+ String height;
+
+ public String toString()
+ {
+ return "Image: url: ${url}, width:${width}, height: ${height}";
+ }
+}
View
12 src/groovy/org/fogbeam/quoddy/integration/activitystream/Object.groovy
@@ -0,0 +1,12 @@
+package org.fogbeam.quoddy.integration.activitystream
+
+class Object
+{
+ String url;
+ String id;
+
+ public String toString()
+ {
+ return "Object: url: ${url}, id: ${id}";
+ }
+}
View
14 src/groovy/org/fogbeam/quoddy/integration/activitystream/Target.groovy
@@ -0,0 +1,14 @@
+package org.fogbeam.quoddy.integration.activitystream
+
+class Target
+{
+ String url;
+ String objectType;
+ String id;
+ String displayName;
+
+ public String toString()
+ {
+ return "Target: url: ${url}, objectType: ${objectType}, id: ${id}, displayName: ${displayName}";
+ }
+}

0 comments on commit 7e14c53

Please sign in to comment.
Something went wrong with that request. Please try again.