SHINDIG-1329 | Patch from Eric Woods | ActivityStreams support git-svn-id: https://svn.apache.org/repos/asf/shindig/branches/activitystreams-prototype@944438 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/content/samplecontainer/examples/ActivityStreams/ActivityStreamGadget.xml b/content/samplecontainer/examples/ActivityStreams/ActivityStreamGadget.xml new file mode 100644 index 0000000..ea7ad42 --- /dev/null +++ b/content/samplecontainer/examples/ActivityStreams/ActivityStreamGadget.xml
@@ -0,0 +1,201 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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. +--> +<Module> + <ModulePrefs title="ActivityStreams Gadget!"> + <Require feature="opensocial-0.9"/> + <Require feature="osapi"/> + <Require feature="dynamic-height"/> + </ModulePrefs> + + <Content type="html"> + <![CDATA[ + <script type="text/javascript" src="ActivityStreams.js"></script> + + <script type="text/javascript"> + // Stores the HTML for rendering + var htmlWelcome = ""; + var htmlActivities = ""; + var htmlActivityEntries = ""; + var htmlCreateActivityEntry = ""; + var htmlDeleteActivityEntry = ""; + var htmlGetActivityEntryId = ""; + + // ================= PEOPLE ================= + // Composes a list of friends + function displayWelcome() { + // Load the viewer, owner, and friends + ActivityStreams.loadPeople(); + alert("Loading friends..."); + + // Compose HTML + htmlWelcome = "Hi, " + viewer.getDisplayName() + "! You are viewing " + owner.getDisplayName() + "'s data. <br><br>"; + htmlWelcome += "Here is a list of your friends: <br>"; + htmlWelcome += "<lu>"; + viewerFriends.each(function(friend) { + htmlWelcome += "<li>" + friend.getDisplayName() + "</li>"; + }); + htmlWelcome += "</lu>"; + } + + // ================= ACTIVITY STREAMS ================= + // Form to input ActivityEntry + function displayCreateActivityEntry() { + htmlCreateActivityEntry = "<br><form name='form_postActivityEntry'>" + htmlCreateActivityEntry += "Post an ActivityEntry comment:<br>" + htmlCreateActivityEntry += "<input type='text' size=100 name ='activityEntryComment'>" + htmlCreateActivityEntry += "<input type='button' value='Post!' onclick='createActivityEntry(document.form_postActivityEntry.activityEntryComment.value)'>" + htmlCreateActivityEntry += "</form>" + } + + function createActivityEntry(comment) { + ActivityStreams.postActivityEntry(comment); + displayActivityEntries(); + refreshHTML(); + } + + function displayDeleteActivityEntry() { + htmlDeleteActivityEntry = "<br><form name='form_deleteActivityEntry'>" + htmlDeleteActivityEntry += "Delete an ActivityEntry by ID:<br>" + htmlDeleteActivityEntry += "<input type='text' size=100 name ='activityEntryId'>" + htmlDeleteActivityEntry += "<input type='button' value='Delete!' onclick='deleteActivityEntry(document.form_deleteActivityEntry.activityEntryId.value)'>" + htmlDeleteActivityEntry += "</form>" + } + + function deleteActivityEntry(activityEntryId) { + alert('Deleting Id: ' + activityEntryId); + ActivityStreams.deleteActivityEntry(activityEntryId); + displayActivityEntries(); + refreshHTML(); + } + + function displayGetActivityEntryId() { + htmlGetActivityEntryId = "<br><form name='form_getActivityEntryId'>" + htmlGetActivityEntryId += "Get an ActivityEntry by ID:<br>" + htmlGetActivityEntryId += "<input type='text' size=100 name ='activityEntryId'>" + htmlGetActivityEntryId += "<input type='button' value='Get!' onclick='getActivityEntryId(document.form_getActivityEntryId.activityEntryId.value)'>" + htmlGetActivityEntryId += "</form>" + } + + function getActivityEntryId(activityEntryId) { + alert('Getting Id: ' + activityEntryId); + ActivityStreams.loadActivityEntryId(activityEntryId); + displayActivities(); + refreshHTML(); + } + + function displayActivityEntries() { + // Load recent activityentries + ActivityStreams.loadActivityEntriesFriends(); + ActivityStreams.loadActivityEntriesViewer(); + alert('Loading ActivityEntries...'); + + // Compose HTML + htmlActivityEntries = "<br><br>All ActivityEntries: <br>"; + htmlActivityEntries += "<table border='1'>"; + htmlActivityEntries += "<tr>"; + htmlActivityEntries += "<td>Title</td>"; + htmlActivityEntries += "<td>Body</td>"; + htmlActivityEntries += "<td>Actor</td>"; + htmlActivityEntries += "<td>ID</td>"; + htmlActivityEntries += "<td>Posted</td>"; + htmlActivityEntries += "<td>Text</td>"; + htmlActivityEntries += "</tr>" + processActivityEntries(viewerActivityEntries); + processActivityEntries(friendActivityEntries); + htmlActivityEntries += "</table>"; + } + + function processActivityEntries(activityentries) { + for(idx = 0; idx < activityentries.list.length; idx++) { + htmlActivityEntries += "<tr>"; + htmlActivityEntries += "<td>" + activityentries.list[idx].title + "</td>"; + htmlActivityEntries += "<td>" + activityentries.list[idx].body + "</td>"; + htmlActivityEntries += "<td>" + activityentries.list[idx].actor.displayName + "</td>"; + htmlActivityEntries += "<td>" + activityentries.list[idx].id + "</td>"; + htmlActivityEntries += "<td>" + activityentries.list[idx].postedTime + "</td>"; + htmlActivityEntries += "<td><textarea>" + JSON.stringify(activityentries.list[idx]) + "</textarea></td>"; + htmlActivityEntries += "</tr>"; + } + } + + // ================= ACTIVITIES ================= + // Composes the list of activities by the viewer and friends + function displayActivities() { + // Load recent activities + ActivityStreams.loadActivitiesFriends(); + ActivityStreams.loadActivitiesViewer(); + alert('Loading Activities...'); + + // Compose HTML + htmlActivities = "<br><br>All Activities: <br>"; + htmlActivities += "<table border='1'>"; + htmlActivities += "<tr>"; + htmlActivities += "<td>Name</td>"; + htmlActivities += "<td>Title</td>"; + htmlActivities += "<td>Body</td>"; + htmlActivities += "<td>Images</td>"; + htmlActivities += "</tr>" + processActivities(viewerActivities); + processActivities(friendActivities); + htmlActivities += "</table>"; + } + + function processActivities(activities) { + for(idx = 0; idx < activities.list.length; idx++) { + htmlActivities += "<tr>"; + htmlActivities += "<td>" + activities.list[idx].userId + "</td>"; + htmlActivities += "<td>" + activities.list[idx].title + "</td>"; + htmlActivities += "<td>" + activities.list[idx].body + "</td>"; + var mediaItems = activities.list[idx].mediaItems; + if(mediaItems != null) { + for(itemIdx = 0; itemIdx < mediaItems.length; itemIdx++) { + if(mediaItems[itemIdx].type == "image") { + htmlActivities += "<td><img src='" + mediaItems[itemIdx].url + "' width=150 height=150/></td>"; + } + } + } + htmlActivities += "</tr>"; + } + } + + // =================== GADGET SET UP ================ + // Refreshes the HTML content + function refreshHTML() { + document.getElementById('message').innerHTML = htmlWelcome + htmlActivities + htmlActivityEntries + htmlCreateActivityEntry + htmlDeleteActivityEntry + htmlGetActivityEntryId; + gadgets.window.adjustHeight(); + } + + // Initializes the gadget + function init() { + displayWelcome(); + displayActivities(); + displayActivityEntries(); + displayCreateActivityEntry(); + displayDeleteActivityEntry(); + displayGetActivityEntryId(); + refreshHTML(); + } + + gadgets.util.registerOnLoadHandler(init); + </script> + <div id='message'></div> + ]]> + </Content> +</Module> \ No newline at end of file
diff --git a/content/samplecontainer/examples/ActivityStreams/ActivityStreams.js b/content/samplecontainer/examples/ActivityStreams/ActivityStreams.js new file mode 100644 index 0000000..cc80904 --- /dev/null +++ b/content/samplecontainer/examples/ActivityStreams/ActivityStreams.js
@@ -0,0 +1,197 @@ +/** + * 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. + */ + +var ActivityStreams = new function() { + + // =============================== PEOPLE =============================== + // Loads viewer and friends + this.loadPeople = function() { + var req = opensocial.newDataRequest(); + req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), 'viewer'); + req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.OWNER), 'owner'); + var idSpec = opensocial.newIdSpec({'userId':'VIEWER', 'groupId':'FRIENDS'}); + req.add(req.newFetchPeopleRequest(idSpec), 'viewerFriends'); + req.send(this.onLoadPeople); + } + + // Callback for loading viewer and friends + this.onLoadPeople = function(response) { + viewer = response.get('viewer').getData(); // Load viewer (type: person) + owner = response.get('owner').getData(); // Load owner (type: person) + viewerFriends = response.get('viewerFriends').getData(); // Load viewer's friends (type: Collection<Person>) + CommentStream.renderWelcome(); + } + + // Loads viewer and friends using OSAPI + this.loadFriendsOsapi = function() { + var batch = osapi.newBatch(); + batch.add('viewer', osapi.people.getViewer()); + batch.add('viewerFriends', osapi.people.getViewerFriends()); + batch.execute(this.onLoadFriendsOsapi); + } + + // Callback for loading viewer and friends using OSAPI + this.onLoadFriendsOsapi = function(response) { + viewer = response.viewer; // Type: JSON object + viewerFriends = response.viewerFriends; // Type: JSON object + alert(JSON.stringify(response)); // Prints the raw JSON response :D + } + + // ========================= ACTIVITY STREAMS ============================= + // Creates and sends an ActivityEntry + this.postActivityEntry = function(comment) { + var params = { + userId: '@viewer', + groupId: '@self', + activityEntry: { + id: 'myEntryId', + permalink: 'http://www.myactivityentry1.com', + postedTime: '2010-04-27T06:02:36+0000', + title: 'My Comment', + body: comment, + actor: { + id: 'john.doe', + displayName: 'Eric Woods' + }, + verb: ['play', 'post'], + object: { + id: 'activityObjectID', + displayName: 'My Object', + permalinkUrl: 'http://www.myobject.com', + objectType: ['event', 'meetup'] + } + } + } + + // Send the request and register callback + osapi.activitystreams.create(params).execute(this.onPostActivityEntry); + } + this.onPostActivityEntry = function(response) { + //alert('onPostActivityEntry: ' + JSON.stringify(response)); + } + + // Deletes the ActivityEntry with the given id + this.deleteActivityEntry = function(activityEntryId) { + // Generate request + var params = { + userId: '@viewer', + groupId: '@self', + activityEntryId: activityEntryId + } + + // Send request + osapi.activitystreams.delete(params).execute(this.onDeleteActivityEntry); + } + this.onDeleteActivityEntry = function(response) { + //alert('onDeleteActivityEntry: ' + JSON.stringify(response)); + } + + // Loads the ActivityEntries of the viewer + this.loadActivityEntriesViewer = function() { + var params = {userId: '@viewer', groupId: '@self'} + osapi.activitystreams.get(params).execute(this.onLoadActivityEntriesViewer); + } + this.onLoadActivityEntriesViewer = function(response) { + viewerActivityEntries = response; + //alert('onLoadActivityEntriesViewer: ' + JSON.stringify(response)); + } + + // Loads the ActivityEntries of the viewer's friends + this.loadActivityEntriesFriends = function() { + var params = {userId: '@viewer', groupId: '@friends'} + osapi.activitystreams.get(params).execute(this.onLoadActivityEntriesFriends); + } + this.onLoadActivityEntriesFriends = function(response) { + //alert('onLoadActivityEntriesFriends: ' + JSON.stringify(response)); + friendActivityEntries = response; + } + + // Loads the ActivityEntry with the given ID + this.loadActivityEntryId = function(activityEntryId) { + var params = {activityEntryIds: ['myEntryID', activityEntryId]}; + osapi.activitystreams.get(params).execute(this.onLoadActivityEntryId); + } + this.onLoadActivityEntryId = function(response) { + alert('onLoadActivityEntryId: ' + JSON.stringify(response)); + } + + // ============================== ACTIVITIES ============================== + // Gets the activities of the viewer + this.loadActivitiesViewer = function() { + var req = osapi.activities.get({userId: '@viewer', groupId: '@self'}); + req.execute(this.onLoadActivitiesViewer); + } + + // Callback to get the activities of the viewer + this.onLoadActivitiesViewer = function(response) { + viewerActivities = response; + //alert(JSON.stringify(response)); // Prints the raw JSON response :D + } + + // Gets the activities of the viewer's friends + this.loadActivitiesFriends = function() { + var req = osapi.activities.get({userId: '@viewer', groupId: '@friends'}); + req.execute(this.onLoadActivitiesFriends); + } + + // Callback to get the activities of the viewer's friends + this.onLoadActivitiesFriends = function(response) { + friendActivities = response; + //alert(JSON.stringify(response)); // Prints the raw JSON response :D + } + + // Creates and sends an activity. + this.postActivity = function(title, body, photoURL, photoUploaded) { + //alert('postActivity(' + title + ', ' + body + ',' + photoURL + ',' + photoUploaded + ')'); + + if(false && photoURL != '') { + alert('uploading image at URL: ' + photoURL); + + var params = {}; + params[opensocial.MediaItem.Field.MIME_TYPE] = 'image/jpeg'; + params[opensocial.MediaItem.Field.TYPE] = opensoical.MediaItem.Type.IMAGE; + params[opensocial.MediaItem.Field.URL] = photoURL; + var media = opensocial.newMediaItem('image/jpeg', photoURL, params); + var req = opensocial.newDataRequest(); + var idSpec = opensocial.newIdSpec({'userId':'VIEWER', 'groupId':'FRIENDS'}); + req.add(req.newCreateMediaItemRequest(idSpec, '1', media), 'media'); + req.send(function(response) { + alert('respone!'); + alert(JSON.stringify(response)); + }); + } + + var params = { + auth: {"default" : null, "type" : "AuthToken"}, + userId: '@viewer', + groupId: '@self', + activity: { + userId: viewer.getId(), + title: title, + body: body, + updated: '2009-06-01T12:54:00Z' + } + }; + osapi.activities.create(params).execute(this.onPostActivity); + } + + // Callback for posting activities + this.onPostActivity = function(response) { + alert(JSON.stringify(response)); // Prints the raw JSON response :D + } +} \ No newline at end of file
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java b/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java index 195eaa8..5c6768c 100644 --- a/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java +++ b/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java
@@ -30,6 +30,7 @@ import org.apache.shindig.social.core.util.BeanXStreamAtomConverter; import org.apache.shindig.social.core.util.xstream.XStream081Configuration; import org.apache.shindig.social.opensocial.service.ActivityHandler; +import org.apache.shindig.social.opensocial.service.ActivityStreamHandler; import org.apache.shindig.social.opensocial.service.AppDataHandler; import org.apache.shindig.social.opensocial.service.MessageHandler; import org.apache.shindig.social.opensocial.service.PersonHandler; @@ -82,7 +83,11 @@ * to add or replace additional handlers. */ protected Set<Class<?>> getHandlers() { - return ImmutableSet.<Class<?>>of(ActivityHandler.class, AppDataHandler.class, - PersonHandler.class, MessageHandler.class); + return ImmutableSet.<Class<?>>of( + ActivityHandler.class, + AppDataHandler.class, + PersonHandler.class, + MessageHandler.class, + ActivityStreamHandler.class ); } }
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/core/model/ActivityEntryImpl.java b/java/social-api/src/main/java/org/apache/shindig/social/core/model/ActivityEntryImpl.java new file mode 100644 index 0000000..5e52acc --- /dev/null +++ b/java/social-api/src/main/java/org/apache/shindig/social/core/model/ActivityEntryImpl.java
@@ -0,0 +1,131 @@ +/* + * 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.shindig.social.core.model; + +import java.util.List; + +import org.apache.shindig.social.opensocial.model.ActivityEntry; +import org.apache.shindig.social.opensocial.model.ActivityObject; + +public class ActivityEntryImpl implements ActivityEntry { + + private String id; + private String streamFavicon; + private String postedTime; + private ActivityObject actor; + private List<String> verb; + private ActivityObject object; + private ActivityObject target; + private String permalink; + private String title; + private String body; + + public ActivityEntryImpl() { + this.id = null; + this.streamFavicon = null; + this.postedTime = null; + this.actor = null; + this.verb = null; + this.object = null; + this.target = null; + this.permalink = null; + this.title = null; + this.body = null; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setStreamFavicon(String streamFavicon) { + this.streamFavicon = streamFavicon; + } + + public String getStreamFavicon() { + return streamFavicon; + } + + public void setPostedTime(String postedTime) { + this.postedTime = postedTime; + } + + public String getPostedTime() { + return postedTime; + } + + public void setActor(ActivityObject actor) { + this.actor = actor; + } + + public ActivityObject getActor() { + return actor; + } + + public void setVerb(List<String> verb) { + this.verb = verb; + } + + public List<String> getVerb() { + return verb; + } + + public void setObject(ActivityObject object) { + this.object = object; + } + + public ActivityObject getObject() { + return object; + } + + public void setTarget(ActivityObject target) { + this.target = target; + } + + public ActivityObject getTarget() { + return target; + } + + public void setPermalink(String permalink) { + this.permalink = permalink; + } + + public String getPermalink() { + return permalink; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getTitle() { + return title; + } + + public void setBody(String body) { + this.body = body; + } + + public String getBody() { + return body; + } +}
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/core/model/ActivityObjectImpl.java b/java/social-api/src/main/java/org/apache/shindig/social/core/model/ActivityObjectImpl.java new file mode 100644 index 0000000..70f415c --- /dev/null +++ b/java/social-api/src/main/java/org/apache/shindig/social/core/model/ActivityObjectImpl.java
@@ -0,0 +1,200 @@ +/* + * 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.shindig.social.core.model; + +import java.util.List; + +import org.apache.shindig.social.opensocial.model.ActivityObject; + +public class ActivityObjectImpl implements ActivityObject { + + private String id; + private String displayName; + private String summary; + private String permalink; + private String image; + private List<String> objectType; + private String content; + private String audioStream; + private String videoStream; + private String playerApplet; + private String bookmarkTarget; + private String thumbnail; + private String subject; + private String description; + private String rating; + private String icon; + private ActivityObject inReplyTo; + + public ActivityObjectImpl() { + this.id = null; + this.displayName = null; + this.summary = null; + this.permalink = null; + this.image = null; + this.content = null; + this.audioStream = null; + this.videoStream = null; + this.playerApplet = null; + this.bookmarkTarget = null; + this.thumbnail = null; + this.subject = null; + this.description = null; + this.rating = null; + this.icon = null; + this.objectType = null; + this.inReplyTo = null; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getPermalink() { + return permalink; + } + + public void setPermalink(String permalink) { + this.permalink = permalink; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getAudioStream() { + return audioStream; + } + + public void setAudioStream(String audioStream) { + this.audioStream = audioStream; + } + + public String getVideoStream() { + return videoStream; + } + + public void setVideoStream(String videoStream) { + this.videoStream = videoStream; + } + + public String getPlayerApplet() { + return playerApplet; + } + + public void setPlayerApplet(String playerApplet) { + this.playerApplet = playerApplet; + } + + public String getBookmarkTarget() { + return bookmarkTarget; + } + + public void setBookmarkTarget(String bookmarkTarget) { + this.bookmarkTarget = bookmarkTarget; + } + + public String getThumbnail() { + return thumbnail; + } + + public void setThumbnail(String thumbnail) { + this.thumbnail = thumbnail; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getRating() { + return rating; + } + + public void setRating(String rating) { + this.rating = rating; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public List<String> getObjectType() { + return objectType; + } + + public void setObjectType(List<String> objectType) { + this.objectType = objectType; + } + + public ActivityObject getInReplyTo() { + return inReplyTo; + } + + public void setInReplyTo(ActivityObject inReplyTo) { + this.inReplyTo = inReplyTo; + } +}
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/core/model/ActivityStreamImpl.java b/java/social-api/src/main/java/org/apache/shindig/social/core/model/ActivityStreamImpl.java new file mode 100644 index 0000000..9af3932 --- /dev/null +++ b/java/social-api/src/main/java/org/apache/shindig/social/core/model/ActivityStreamImpl.java
@@ -0,0 +1,81 @@ +/* + * 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.shindig.social.core.model; + +import java.util.List; + +import org.apache.shindig.social.opensocial.model.ActivityEntry; +import org.apache.shindig.social.opensocial.model.ActivityStream; + +public class ActivityStreamImpl implements ActivityStream { + + private String displayName; + private String language; + private List<ActivityEntry> entries; + private String id; + private String subject; + + public ActivityStreamImpl() { + this.displayName = null; + this.language = null; + this.entries = null; + this.id = null; + this.subject = null; + } + + public String getDisplayName() { + return displayName; + } + + public List<ActivityEntry> getEntries() { + return entries; + } + + public String getId() { + return id; + } + + public String getLanguage() { + return language; + } + + public String getSubject() { + return subject; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public void setEntries(List<ActivityEntry> entries) { + this.entries = entries; + } + + public void setId(String id) { + this.id = id; + } + + public void setLanguage(String language) { + this.language = language; + } + + public void setSubject(String subject) { + this.subject = subject; + } +}
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/core/util/xstream/XStream081Configuration.java b/java/social-api/src/main/java/org/apache/shindig/social/core/util/xstream/XStream081Configuration.java index 915b7c8..6b00f93 100644 --- a/java/social-api/src/main/java/org/apache/shindig/social/core/util/xstream/XStream081Configuration.java +++ b/java/social-api/src/main/java/org/apache/shindig/social/core/util/xstream/XStream081Configuration.java
@@ -17,6 +17,12 @@ */ package org.apache.shindig.social.core.util.xstream; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + import org.apache.shindig.protocol.DataCollection; import org.apache.shindig.protocol.RestfulCollection; import org.apache.shindig.protocol.conversion.xstream.ClassFieldMapping; @@ -41,6 +47,7 @@ import org.apache.shindig.social.core.util.atom.AtomLinkConverter; import org.apache.shindig.social.opensocial.model.Account; import org.apache.shindig.social.opensocial.model.Activity; +import org.apache.shindig.social.opensocial.model.ActivityStream; import org.apache.shindig.social.opensocial.model.Address; import org.apache.shindig.social.opensocial.model.BodyType; import org.apache.shindig.social.opensocial.model.ListField; @@ -69,12 +76,6 @@ import com.thoughtworks.xstream.mapper.AttributeMapper; import com.thoughtworks.xstream.mapper.Mapper; -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - /** * Opensocial 0.81 compliant Xstream binding */ @@ -135,6 +136,7 @@ .put("feed", atom) .put("person", os) .put("activity", os) + .put("activityStream", os) .put("account", os) .put("address", os) .put("bodyType", os) @@ -154,6 +156,7 @@ new ClassFieldMapping("content", AtomContent.class), new ClassFieldMapping("activity", Activity.class), + new ClassFieldMapping("activityStream", ActivityStream.class), new ClassFieldMapping("account", Account.class), new ClassFieldMapping("address", Address.class), new ClassFieldMapping("bodyType", BodyType.class), @@ -188,6 +191,7 @@ new ClassFieldMapping("content", AtomContent.class), new ClassFieldMapping("activity", Activity.class), + new ClassFieldMapping("activityStream", ActivityStream.class), new ClassFieldMapping("account", Account.class), new ClassFieldMapping("address", Address.class), new ClassFieldMapping("bodyType", BodyType.class), @@ -223,6 +227,7 @@ .put("map", ConcurrentHashMap.class) .put("appdata", DataCollection.class) .put("activity", Activity.class) + .put("activityStream", ActivityStream.class) .put("account", Account.class) .put("address", Address.class) .put("bodyType", BodyType.class)
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/opensocial/model/ActivityEntry.java b/java/social-api/src/main/java/org/apache/shindig/social/opensocial/model/ActivityEntry.java new file mode 100644 index 0000000..9f4f73c --- /dev/null +++ b/java/social-api/src/main/java/org/apache/shindig/social/opensocial/model/ActivityEntry.java
@@ -0,0 +1,150 @@ +/* + * 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.shindig.social.opensocial.model; + +import java.util.List; + +import org.apache.shindig.protocol.model.Exportablebean; +import org.apache.shindig.social.core.model.ActivityEntryImpl; + +import com.google.inject.ImplementedBy; + +@ImplementedBy(ActivityEntryImpl.class) +@Exportablebean +public interface ActivityEntry { + + /* + * Fields that represent JSON elements for an activity entry. + */ + public static enum Field { + ID("id"), + STREAM_FAVICON_("streamFavicon"), + POSTED_TIME("postedTime"), + ACTOR("actor"), + VERB("verb"), + OBJECT("object"), + TARGET("target"), + PERMALINK_("permalink"), + TITLE("title"), + BODY("body"); + + /* + * The name of the JSON element. + */ + private final String jsonString; + + /* + * Constructs the field base for the JSON element. + * + * @param jsonString the name of the element + */ + private Field(String jsonString) { + this.jsonString = jsonString; + } + + /* + * Returns the name of the JSON element. + * + * @return String the name of the JSON element + */ + public String toString() { + return jsonString; + } + } + + /* + * Possible verbs for an activity stream entry. + */ + public static enum Verb { + MARK_AS_FAVORITE("markAsFavorite"), + START_FOLLOWING("startFollowing"), + MARK_AS_LIKED("markAsLiked"), + MAKE_FRIEND("makeFriend"), + JOIN("join"), + PLAY("play"), + POST("post"), + SAVE("save"), + SHARE("share"), + TAG("tag"), + UPDATE("update"); + + /* + * The name of the JSON element. + */ + private final String jsonString; + + /* + * Constructs the field base for the JSON element. + * + * @param jsonString the name of the element + */ + private Verb(String jsonString) { + this.jsonString = jsonString; + } + + /* + * Returns the name of the JSON element. + * + * @return String the name of the JSON element + */ + public String toString() { + return jsonString; + } + } + + void setId(String id); + + String getId(); + + void setStreamFavicon(String streamFavicon); + + String getStreamFavicon(); + + void setPostedTime(String postedTime); + + String getPostedTime(); + + void setActor(ActivityObject actor); + + ActivityObject getActor(); + + void setVerb(List<String> verb); + + List<String> getVerb(); + + void setObject(ActivityObject object); + + ActivityObject getObject(); + + void setTarget(ActivityObject target); + + ActivityObject getTarget(); + + void setPermalink(String permalink); + + String getPermalink(); + + void setTitle(String title); + + String getTitle(); + + void setBody(String body); + + String getBody(); +} \ No newline at end of file
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/opensocial/model/ActivityObject.java b/java/social-api/src/main/java/org/apache/shindig/social/opensocial/model/ActivityObject.java new file mode 100644 index 0000000..193276c --- /dev/null +++ b/java/social-api/src/main/java/org/apache/shindig/social/opensocial/model/ActivityObject.java
@@ -0,0 +1,145 @@ +/* + * 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.shindig.social.opensocial.model; + +import java.util.List; + +import org.apache.shindig.protocol.model.Exportablebean; +import org.apache.shindig.social.core.model.ActivityObjectImpl; + +import com.google.inject.ImplementedBy; + +@ImplementedBy(ActivityObjectImpl.class) +@Exportablebean +public interface ActivityObject { + + /* + * Fields that represent JSON elements for an activity entry. + */ + public static enum Field { + ID("id"), + DISPLAY_NAME("displayName"), + SUMMARY("summary"), + PERMALINK("permalink"), + IMAGE("image"), + CONTENT("content"), + AUDIO_STREAM("audioStream"), + VIDEO_STREAM("videoStream"), + PLAYER_APPLET("playerApplet"), + BOOKMARK_TARGET("bookmarkTarget"), + THUMBNAIL("thumbnail"), + SUBJECT("subject"), + DESCRIPTION("description"), + RATING("rating"), + ICON("icon"), + IN_REPLY_TO("inReplyTo"), + OBJECT_TYPE("objectType"); + + /* + * The name of the JSON element. + */ + private final String jsonString; + + /* + * Constructs the field base for the JSON element. + * + * @param jsonString the name of the element + */ + private Field(String jsonString) { + this.jsonString = jsonString; + } + + /* + * Returns the name of the JSON element. + * + * @return String the name of the JSON element + */ + public String toString() { + return jsonString; + } + } + + String getId(); + + void setId(String id); + + String getDisplayName(); + + void setDisplayName(String displayName); + + String getSummary(); + + void setSummary(String summary); + + String getPermalink(); + + void setPermalink(String permalink); + + String getImage(); + + void setImage(String image); + + String getContent(); + + void setContent(String content); + + String getAudioStream(); + + void setAudioStream(String audioStream); + + String getVideoStream(); + + void setVideoStream(String videoStream); + + String getPlayerApplet(); + + void setPlayerApplet(String playerApplet); + + String getBookmarkTarget(); + + void setBookmarkTarget(String bookmarkTarget); + + String getThumbnail(); + + void setThumbnail(String thumbnail); + + String getSubject(); + + void setSubject(String subject); + + String getDescription(); + + void setDescription(String description); + + String getRating(); + + void setRating(String rating); + + String getIcon(); + + void setIcon(String icon); + + List<String> getObjectType(); + + void setObjectType(List<String> objectType); + + ActivityObject getInReplyTo(); + + void setInReplyTo(ActivityObject inReplyTo); +} \ No newline at end of file
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/opensocial/model/ActivityStream.java b/java/social-api/src/main/java/org/apache/shindig/social/opensocial/model/ActivityStream.java new file mode 100644 index 0000000..3439425 --- /dev/null +++ b/java/social-api/src/main/java/org/apache/shindig/social/opensocial/model/ActivityStream.java
@@ -0,0 +1,91 @@ +/* + * 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.shindig.social.opensocial.model; + +import java.util.List; + +import org.apache.shindig.protocol.model.Exportablebean; +import org.apache.shindig.social.core.model.ActivityStreamImpl; + +import com.google.inject.ImplementedBy; + +/* + * Interface for an Activity Stream. + * <p> + * See the Activity Streams specification for more detail: + * http://activitystrea.ms/ + */ +@ImplementedBy(ActivityStreamImpl.class) +@Exportablebean +public interface ActivityStream { + + /* + * Fields that represent JSON elements for an activity entry. + */ + public static enum Field { + DISPLAY_NAME("displayName"), + LANGUAGE("language"), + ENTRIES("entries"), + ID("id"), + SUBJECT("subject"); + + /* + * The name of the JSON element. + */ + private final String jsonString; + + /* + * Constructs the field base for the JSON element. + * + * @param jsonString the name of the element + */ + private Field(String jsonString) { + this.jsonString = jsonString; + } + + /* + * Returns the name of the JSON element. + * + * @return String the name of the JSON element + */ + public String toString() { + return jsonString; + } + } + + void setId(String id); + + String getId(); + + void setEntries(List<ActivityEntry> entries); + + List<ActivityEntry> getEntries(); + + void setLanguage(String language); + + String getLanguage(); + + void setSubject(String subject); + + String getSubject(); + + void setDisplayName(String displayName); + + String getDisplayName(); +} \ No newline at end of file
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityStreamHandler.java b/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityStreamHandler.java new file mode 100644 index 0000000..63b78cd --- /dev/null +++ b/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityStreamHandler.java
@@ -0,0 +1,156 @@ +/* + * 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.shindig.social.opensocial.service; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.Future; + +import org.apache.shindig.config.ContainerConfig; +import org.apache.shindig.protocol.HandlerPreconditions; +import org.apache.shindig.protocol.Operation; +import org.apache.shindig.protocol.ProtocolException; +import org.apache.shindig.protocol.RequestItem; +import org.apache.shindig.protocol.Service; +import org.apache.shindig.social.opensocial.model.ActivityEntry; +import org.apache.shindig.social.opensocial.spi.ActivityStreamService; +import org.apache.shindig.social.opensocial.spi.CollectionOptions; +import org.apache.shindig.social.opensocial.spi.UserId; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.inject.Inject; + +@Service(name = "activitystreams", path="/{userId}+/{groupId}/{appId}/{activitystream}/{activityEntryId}+") +public class ActivityStreamHandler { + + private final ActivityStreamService service; + private final ContainerConfig config; + + @Inject + public ActivityStreamHandler(ActivityStreamService service, ContainerConfig config) { + this.service = service; + this.config = config; + } + + /** + * Allowed end-points /activities/{userId}/@self/{actvityId}+ + * + * examples: /activities/john.doe/@self/1 + */ + @Operation(httpMethods="DELETE") + public Future<?> delete(SocialRequestItem request) + throws ProtocolException { + + Set<UserId> userIds = request.getUsers(); + Set<String> activityEntryIds = ImmutableSet.copyOf(request.getListParameter("activityEntryId")); + + HandlerPreconditions.requireNotEmpty(userIds, "No userId specified"); + HandlerPreconditions.requireSingular(userIds, "Multiple userIds not supported"); + // Throws exceptions if userIds contains more than one element or zero elements + return service.deleteActivityEntries(Iterables.getOnlyElement(userIds), request.getGroup(), + request.getAppId(), activityEntryIds, request.getToken()); + } + + /** + * Allowed end-points /activities/{userId}/@self + * + * examples: /activities/john.doe/@self - postBody is an activity object + * + * TODO: REST end-point + */ + @Operation(httpMethods="PUT", bodyParam = "activityEntry") + public Future<?> update(SocialRequestItem request) throws ProtocolException { + return create(request); + } + + /** + * Allowed end-points /activities/{userId}/@self + * + * examples: /activities/john.doe/@self - postBody is an activity object + * + * TODO: REST end-point + */ + @Operation(httpMethods="POST", bodyParam = "activityEntry") + public Future<?> create(SocialRequestItem request) throws ProtocolException { + Set<UserId> userIds = request.getUsers(); + List<String> activityEntryIds = request.getListParameter("activityEntryId"); + + HandlerPreconditions.requireNotEmpty(userIds, "No userId specified"); + HandlerPreconditions.requireSingular(userIds, "Multiple userIds not supported"); + // TODO(lryan) This seems reasonable to allow on PUT but we don't have an update verb. + HandlerPreconditions.requireEmpty(activityEntryIds, "Cannot specify activityEntryId in create"); + + return service.createActivityEntry(Iterables.getOnlyElement(userIds), request.getGroup(), + request.getAppId(), request.getFields(), + request.getTypedParameter("activityEntry", ActivityEntry.class), + request.getToken()); + } + + /** + * Allowed end-points /activities/{userId}/{groupId}/{optionalActvityId}+ + * /activities/{userId}+/{groupId} + * + * examples: /activities/john.doe/@self/1 /activities/john.doe/@self + * /activities/john.doe,jane.doe/@friends + */ + @Operation(httpMethods="GET") + public Future<?> get(SocialRequestItem request) + throws ProtocolException { + Set<UserId> userIds = request.getUsers(); + Set<String> optionalActivityIds = ImmutableSet.copyOf(request.getListParameter("activityEntryId")); + + CollectionOptions options = new CollectionOptions(request); + + // Preconditions + HandlerPreconditions.requireNotEmpty(userIds, "No userId specified"); + if (userIds.size() > 1 && !optionalActivityIds.isEmpty()) { + throw new IllegalArgumentException("Cannot fetch same activityEntryIds for multiple userIds"); + } + + if (!optionalActivityIds.isEmpty()) { + if (optionalActivityIds.size() == 1) { + return service.getActivityEntry(userIds.iterator().next(), request.getGroup(), + request.getAppId(), request.getFields(), optionalActivityIds.iterator().next(), + request.getToken()); + } else { + return service.getActivityEntries(userIds.iterator().next(), request.getGroup(), + request.getAppId(), request.getFields(), options, optionalActivityIds, request.getToken()); + } + } + + return service.getActivityEntries(userIds, request.getGroup(), + request.getAppId(), + // TODO: add pagination and sorting support + // getSortBy(params), getFilterBy(params), getStartIndex(params), getCount(params), + request.getFields(), options, request.getToken()); + } + + @Operation(httpMethods = "GET", path="/@supportedFields") + public List<Object> supportedFields(RequestItem request) { + // TODO: Would be nice if name in config matched name of service. + String container = firstNonNull(request.getToken().getContainer(), ContainerConfig.DEFAULT_CONTAINER); + return config.getList(container, + "${Cur['gadgets.features'].opensocial.supportedFields.activitystream}"); + } + + private static <T> T firstNonNull(T first, T second) { + return first != null ? first : Preconditions.checkNotNull(second); + } +}
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/ActivityStreamService.java b/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/ActivityStreamService.java new file mode 100644 index 0000000..193a762 --- /dev/null +++ b/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/ActivityStreamService.java
@@ -0,0 +1,111 @@ +/* + * 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.shindig.social.opensocial.spi; + +import java.util.Set; +import java.util.concurrent.Future; + +import org.apache.shindig.auth.SecurityToken; +import org.apache.shindig.protocol.ProtocolException; +import org.apache.shindig.protocol.RestfulCollection; +import org.apache.shindig.social.opensocial.model.ActivityEntry; +import org.apache.shindig.social.opensocial.model.ActivityStream; + +/** + * The ActivityService interface defines the service provider interface to retrieve activities from + * the underlying SNS. + */ +public interface ActivityStreamService { + + /** + * Returns a list of activities that correspond to the passed in users and group. + * + * @param userIds The set of ids of the people to fetch activities for. + * @param groupId Indicates whether to fetch activities for a group. + * @param appId The app id. + * @param fields The fields to return. Empty set implies all + * @param options The sorting/filtering/pagination options + * @param token A valid SecurityToken + * @return a response item with the list of activities. + */ + Future<RestfulCollection<ActivityEntry>> getActivityEntries(Set<UserId> userIds, + GroupId groupId, String appId, Set<String> fields, CollectionOptions options, SecurityToken token) + throws ProtocolException; + + /** + * Returns a set of activities for the passed in user and group that corresponds to a list of + * activityIds. + * + * @param userId The set of ids of the people to fetch activities for. + * @param groupId Indicates whether to fetch activities for a group. + * @param appId The app id. + * @param fields The fields to return. Empty set implies all + * @param options The sorting/filtering/pagination options + * @param activityIds The set of activity ids to fetch. + * @param token A valid SecurityToken + * @return a response item with the list of activities. + */ + Future<RestfulCollection<ActivityEntry>> getActivityEntries(UserId userId, GroupId groupId, + String appId, Set<String> fields, CollectionOptions options, Set<String> activityIds, SecurityToken token) + throws ProtocolException; + + + /** + * Returns an activity for the passed in user and group that corresponds to a single + * activityId. + * + * @param userId The set of ids of the people to fetch activities for. + * @param groupId Indicates whether to fetch activities for a group. + * @param appId The app id. + * @param fields The fields to return. Empty set implies all + * @param activityId The activity id to fetch. + * @param token A valid SecurityToken + * @return a response item with the list of activities. + */ + Future<ActivityEntry> getActivityEntry(UserId userId, GroupId groupId, String appId, + Set<String> fields, String activityId, SecurityToken token) + throws ProtocolException; + + /** + * Deletes the activity for the passed in user and group that corresponds to the activityId. + * + * @param userId The user. + * @param groupId The group. + * @param appId The app id. + * @param activityIds A list of activity ids to delete. + * @param token A valid SecurityToken. + * @return a response item containing any errors + */ + Future<Void> deleteActivityEntries(UserId userId, GroupId groupId, String appId, + Set<String> activityIds, SecurityToken token) throws ProtocolException; + + /** + * Creates the passed in activity for the passed in user and group. Once createActivity is called, + * getActivities will be able to return the Activity. + * + * @param userId The id of the person to create the activity for. + * @param groupId The group. + * @param appId The app id. + * @param fields The fields to return. + * @param activity The activity to create. + * @param token A valid SecurityToken + * @return a response item containing any errors + */ + Future<Void> createActivityEntry(UserId userId, GroupId groupId, String appId, + Set<String> fields, ActivityEntry activity, SecurityToken token) throws ProtocolException; +}
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java b/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java index 494d918..ae036c3 100644 --- a/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java +++ b/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java
@@ -19,6 +19,7 @@ import org.apache.shindig.social.opensocial.oauth.OAuthDataStore; import org.apache.shindig.social.opensocial.spi.ActivityService; +import org.apache.shindig.social.opensocial.spi.ActivityStreamService; import org.apache.shindig.social.opensocial.spi.AppDataService; import org.apache.shindig.social.opensocial.spi.MessageService; import org.apache.shindig.social.opensocial.spi.PersonService; @@ -43,6 +44,7 @@ bind(String.class).annotatedWith(Names.named("shindig.canonical.json.db")) .toInstance("sampledata/canonicaldb.json"); bind(ActivityService.class).to(JsonDbOpensocialService.class); + bind(ActivityStreamService.class).to(JsonDbOpensocialService.class); bind(AppDataService.class).to(JsonDbOpensocialService.class); bind(PersonService.class).to(JsonDbOpensocialService.class); bind(MessageService.class).to(JsonDbOpensocialService.class);
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java.orig b/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java.orig new file mode 100644 index 0000000..494d918 --- /dev/null +++ b/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java.orig
@@ -0,0 +1,55 @@ +/* + * 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.shindig.social.sample; + +import org.apache.shindig.social.opensocial.oauth.OAuthDataStore; +import org.apache.shindig.social.opensocial.spi.ActivityService; +import org.apache.shindig.social.opensocial.spi.AppDataService; +import org.apache.shindig.social.opensocial.spi.MessageService; +import org.apache.shindig.social.opensocial.spi.PersonService; +import org.apache.shindig.social.sample.oauth.SampleOAuthDataStore; +import org.apache.shindig.social.sample.service.SampleContainerHandler; +import org.apache.shindig.social.sample.spi.JsonDbOpensocialService; + +import com.google.inject.AbstractModule; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.name.Names; + +/** + * Provides bindings for sample-only implementations of social API + * classes. This class should never be used in production deployments, + * but does provide a good overview of the pieces of Shindig that require + * custom container implementations. + */ +public class SampleModule extends AbstractModule { + + @Override + protected void configure() { + bind(String.class).annotatedWith(Names.named("shindig.canonical.json.db")) + .toInstance("sampledata/canonicaldb.json"); + bind(ActivityService.class).to(JsonDbOpensocialService.class); + bind(AppDataService.class).to(JsonDbOpensocialService.class); + bind(PersonService.class).to(JsonDbOpensocialService.class); + bind(MessageService.class).to(JsonDbOpensocialService.class); + + bind(OAuthDataStore.class).to(SampleOAuthDataStore.class); + + Multibinder<Object> handlerBinder = Multibinder.newSetBinder(binder(), Object.class, Names.named("org.apache.shindig.handlers")); + handlerBinder.addBinding().toInstance(SampleContainerHandler.class); + } +}
diff --git a/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java b/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java index 139aaeb..6fdeba5 100644 --- a/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java +++ b/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java
@@ -18,6 +18,16 @@ package org.apache.shindig.social.sample.spi; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Future; + +import javax.servlet.http.HttpServletResponse; + import org.apache.commons.io.IOUtils; import org.apache.shindig.auth.SecurityToken; import org.apache.shindig.common.util.ImmediateFuture; @@ -28,10 +38,14 @@ import org.apache.shindig.protocol.conversion.BeanConverter; import org.apache.shindig.protocol.model.SortOrder; import org.apache.shindig.social.opensocial.model.Activity; +import org.apache.shindig.social.opensocial.model.ActivityEntry; +import org.apache.shindig.social.opensocial.model.ActivityObject; +import org.apache.shindig.social.opensocial.model.ActivityStream; import org.apache.shindig.social.opensocial.model.Message; import org.apache.shindig.social.opensocial.model.MessageCollection; import org.apache.shindig.social.opensocial.model.Person; import org.apache.shindig.social.opensocial.spi.ActivityService; +import org.apache.shindig.social.opensocial.spi.ActivityStreamService; import org.apache.shindig.social.opensocial.spi.AppDataService; import org.apache.shindig.social.opensocial.spi.CollectionOptions; import org.apache.shindig.social.opensocial.spi.GroupId; @@ -42,16 +56,6 @@ import org.json.JSONException; import org.json.JSONObject; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Future; - -import javax.servlet.http.HttpServletResponse; - import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -64,7 +68,7 @@ * Implementation of supported services backed by a JSON DB. */ @Singleton -public class JsonDbOpensocialService implements ActivityService, PersonService, AppDataService, +public class JsonDbOpensocialService implements ActivityService, ActivityStreamService, PersonService, AppDataService, MessageService { private static final Comparator<Person> NAME_COMPARATOR = new Comparator<Person>() { @@ -94,6 +98,11 @@ * db["people"] -> Map<Person.Id, Array<Activity>> */ private static final String ACTIVITIES_TABLE = "activities"; + + /** + * db["people"] -> Map<Person.Id, Array<ActivityEntry>> + */ + private static final String ACTIVITYSTREAMS_TABLE = "activityEntries"; /** * db["data"] -> Map<Person.Id, Map<String, String>> @@ -254,6 +263,143 @@ je); } } + + // Are fields really needed here? + public Future<Void> createActivityEntry(UserId userId, GroupId groupId, String appId, + Set<String> fields, ActivityEntry activityEntry, SecurityToken token) throws ProtocolException { + try { + JSONObject jsonObject = convertFromActivityEntry(activityEntry, fields); + if (!jsonObject.has(ActivityEntry.Field.ID.toString())) { + jsonObject.put(ActivityEntry.Field.ID.toString(), System.currentTimeMillis()); + } + // TODO: bug fixed: jsonArray will not be null; will throw exception! + // Fix in createActivity() + JSONArray jsonArray; + if(db.getJSONObject(ACTIVITYSTREAMS_TABLE).has(userId.getUserId(token))) { + jsonArray = db.getJSONObject(ACTIVITYSTREAMS_TABLE) + .getJSONArray(userId.getUserId(token)); + } else { + jsonArray = new JSONArray(); + db.getJSONObject(ACTIVITYSTREAMS_TABLE).put(userId.getUserId(token), jsonArray); + } + jsonArray.put(jsonObject); + return ImmediateFuture.newInstance(null); + } catch (JSONException je) { + throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, je.getMessage(), + je); + } + } + + public Future<Void> deleteActivityEntries(UserId userId, GroupId groupId, + String appId, Set<String> activityIds, SecurityToken token) + throws ProtocolException { + try { + String user = userId.getUserId(token); + if (db.getJSONObject(ACTIVITYSTREAMS_TABLE).has(user)) { + JSONArray activityEntries = db.getJSONObject(ACTIVITYSTREAMS_TABLE).getJSONArray(user); + if (activityEntries != null) { + JSONArray newList = new JSONArray(); + for (int i = 0; i < activityEntries.length(); i++) { + JSONObject activityEntry = activityEntries.getJSONObject(i); + if (!activityIds.contains(activityEntry.getString(ActivityEntry.Field.ID.toString()))) { + newList.put(activityEntry); + } + } + db.getJSONObject(ACTIVITYSTREAMS_TABLE).put(user, newList); + // TODO: This seems very odd that we return no useful response in this + // case + // There is no way to represent not-found + // if (found) { ?? + // } + } + } + // What is the appropriate response here?? + return ImmediateFuture.newInstance(null); + } catch (JSONException je) { + throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, je.getMessage(), + je); + } + } + + public Future<ActivityEntry> getActivityEntry(UserId userId, GroupId groupId, + String appId, Set<String> fields, String activityId, SecurityToken token) + throws ProtocolException { + try { + String user = userId.getUserId(token); + if (db.getJSONObject(ACTIVITYSTREAMS_TABLE).has(user)) { + JSONArray activityEntries = db.getJSONObject(ACTIVITYSTREAMS_TABLE).getJSONArray(user); + for (int i = 0; i < activityEntries.length(); i++) { + JSONObject activityEntry = activityEntries.getJSONObject(i); + JSONObject actor = new JSONObject(activityEntry.get(ActivityEntry.Field.ACTOR.toString()).toString()); + String actorId = actor.get(ActivityObject.Field.ID.toString()).toString(); + if (actorId.equals(user) + && activityEntry.get(ActivityEntry.Field.ID.toString()).equals(activityId)) { + return ImmediateFuture.newInstance(filterFields(activityEntry, fields, ActivityEntry.class)); + } + } + } + + throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "ActivityEntry not found"); + } catch (JSONException je) { + throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, je.getMessage(), + je); + } + } + + public Future<RestfulCollection<ActivityEntry>> getActivityEntries( + Set<UserId> userIds, GroupId groupId, String appId, Set<String> fields, + CollectionOptions options, SecurityToken token) + throws ProtocolException { + List<ActivityEntry> result = Lists.newArrayList(); + try { + Set<String> idSet = getIdSet(userIds, groupId, token); + for (String id : idSet) { + if (db.getJSONObject(ACTIVITYSTREAMS_TABLE).has(id)) { + JSONArray activityEntries = db.getJSONObject(ACTIVITYSTREAMS_TABLE).getJSONArray(id); + for (int i = 0; i < activityEntries.length(); i++) { + JSONObject activityEntry = activityEntries.getJSONObject(i); + result.add(filterFields(activityEntry, fields, ActivityEntry.class)); + // TODO: ActivityStreams don't have appIds +// if (appId == null || !activitystream.has(ActivityStream.Field.APP_ID.toString())) { +// result.add(filterFields(activitystream, fields, ActivityStream.class)); +// } else if (activitystream.get(ActivityStream.Field.APP_ID.toString()).equals(appId)) { +// result.add(filterFields(activitystream, fields, ActivityStream.class)); +// } + } + } + } + return ImmediateFuture.newInstance(new RestfulCollection<ActivityEntry>(result)); + } catch (JSONException je) { + throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, je.getMessage(), + je); + } + } + + public Future<RestfulCollection<ActivityEntry>> getActivityEntries( + UserId userId, GroupId groupId, String appId, Set<String> fields, + CollectionOptions options, Set<String> activityIds, SecurityToken token) + throws ProtocolException { + List<ActivityEntry> result = Lists.newArrayList(); + try { + String user = userId.getUserId(token); + if (db.getJSONObject(ACTIVITYSTREAMS_TABLE).has(user)) { + JSONArray activityEntries = db.getJSONObject(ACTIVITYSTREAMS_TABLE).getJSONArray(user); + for (int i = 0; i < activityEntries.length(); i++) { + JSONObject activityEntry = activityEntries.getJSONObject(i); + JSONObject actor = new JSONObject(activityEntry.get(ActivityEntry.Field.ACTOR.toString())); + String actorId = actor.get(ActivityObject.Field.ID.toString()).toString(); + if (actorId.equals(user) + && activityIds.contains(activityEntry.getString(ActivityEntry.Field.ID.toString()))) { + result.add(filterFields(activityEntry, fields, ActivityEntry.class)); + } + } + } + return ImmediateFuture.newInstance(new RestfulCollection<ActivityEntry>(result)); + } catch (JSONException je) { + throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, je.getMessage(), + je); + } + } public Future<RestfulCollection<Person>> getPeople(Set<UserId> userIds, GroupId groupId, CollectionOptions options, Set<String> fields, SecurityToken token) throws ProtocolException { @@ -629,6 +775,13 @@ // TODO Not using fields yet return new JSONObject(converter.convertToString(activity)); } + + private JSONObject convertFromActivityEntry(ActivityEntry activityEntry, Set<String> fields) + throws JSONException { + // TODO Not using fields yet + return new JSONObject(converter.convertToString(activityEntry)); + } + private <T> T filterFields(JSONObject object, Set<String> fields, Class<T> clz) throws JSONException {