/**
* Copyright (c) 2011, SOCIETIES Consortium (WATERFORD INSTITUTE OF TECHNOLOGY (TSSG), HERIOT-WATT UNIVERSITY (HWU), SOLUTA.NET
* (SN), GERMAN AEROSPACE CENTRE (Deutsches Zentrum fuer Luft- und Raumfahrt e.V.) (DLR), Zavod za varnostne tehnologije
* informacijske družbe in elektronsko poslovanje (SETCCE), INSTITUTE OF COMMUNICATION AND COMPUTER SYSTEMS (ICCS), LAKE
* COMMUNICATIONS (LAKE), INTEL PERFORMANCE LEARNING SOLUTIONS LTD (INTEL), PORTUGAL TELECOM INOVAÇÃO, SA (PTIN), IBM Corp.,
* INSTITUT TELECOM (ITSUD), AMITEC DIACHYTI EFYIA PLIROFORIKI KAI EPIKINONIES ETERIA PERIORISMENIS EFTHINIS (AMITEC), TELECOM
* ITALIA S.p.a.(TI), TRIALOG (TRIALOG), Stiftelsen SINTEF (SINTEF), NEC EUROPE LTD (NEC))
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.societies.activity;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
import javax.persistence.Table;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import org.apache.shindig.social.opensocial.model.ActivityEntry;
import org.apache.shindig.social.opensocial.model.ActivityObject;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.societies.activity.model.Activity;
import org.societies.activity.model.ActivityString;
import org.societies.api.activity.IActivity;
import org.societies.api.activity.IActivityFeed;
import org.societies.api.activity.IActivityFeedCallback;
import org.societies.api.comm.xmpp.exceptions.CommunicationException;
import org.societies.api.comm.xmpp.exceptions.XMPPError;
import org.societies.api.comm.xmpp.pubsub.PubsubClient;
import org.societies.api.identity.IIdentity;
import org.societies.api.internal.activity.ILocalActivityFeed;
import org.societies.api.schema.activityfeed.AddActivityResponse;
import org.societies.api.schema.activityfeed.CleanUpActivityFeedResponse;
import org.societies.api.schema.activityfeed.DeleteActivityResponse;
import org.societies.api.schema.activityfeed.GetActivitiesResponse;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@Entity
@Table(name = "org_societies_activity_ActivityFeed")
public class ActivityFeed implements IActivityFeed, ILocalActivityFeed {
private String owner;
@Id
private String id;// represents the owner of the activity feed
@OneToMany
private Set<Activity> list;
@Transient
private SessionFactory sessionFactory;
@Transient
private static Logger LOG = LoggerFactory.getLogger(ActivityFeed.class);
private boolean pubSubEnabled;
@Transient
private PubsubClient pubSubcli;
@Transient
private IIdentity ownerCSS;
public ActivityFeed(String id, String owner){
this.owner = owner;
this.id = id;
initClass();
setPubSubEnabled(false);
}
public ActivityFeed(){
initClass();
setPubSubEnabled(false);
}
public void initClass(){
list = new HashSet<Activity>();// from Thomas
}
private final static List<String> classList = Collections
.unmodifiableList( Arrays.asList("org.societies.api.schema.activity.MarshaledActivity"));
// version with PubSub
synchronized public void startUp(SessionFactory sessionFactory){
this.setSessionFactory(sessionFactory);
if (isPubSubEnabled())
this.setPubSubcli(pubSubcli);
}
public void connectPubSub(IIdentity ownerCSS){ //ASSUME PUBSUB NODE PERSISTING (CONFIGURATION), CHECK IF IT EXISTS
this.ownerCSS = ownerCSS;
// pubsub code
if (!isPubSubEnabled()) {
return;
}
LOG.debug("starting pubsub at activityfeed pubsub");
if(null != pubSubcli && null != ownerCSS){
List<String> l;
try {
l = pubSubcli.discoItems(ownerCSS, null);
} catch (XMPPError e) {
LOG.warn("XMPPError at activityfeed pubsub: ",e);
return;
} catch (CommunicationException e) {
LOG.warn("Com at activityfeed pubsub",e);
return;
}
boolean nodeExists = false;
if(l.size() == 0) {
LOG.warn("empty disco item list");
}
if(l != null && l.size()>0){
for(String temp : l){
LOG.warn("Existing node is " + temp);
if (temp.equals(this.getId())) {
nodeExists=true;
}
}
}
if(!nodeExists){
try {
LOG.warn("going to create a pubsub node");
pubSubcli.ownerCreate(ownerCSS, this.getId());
} catch (XMPPError e) {
LOG.warn("XMPPError at activityfeed pubsub: ",e);
} catch (CommunicationException e) {
LOG.warn("Com at activityfeed pubsub: ",e);
}
}else{
LOG.warn("node exists");
}
}
}
public int count(){
Session session = null;
LOG.info("in persistedactivityfeedcount");
int ret = -1;
try {
session = this.getSessionFactory().openSession();
ret = session.createCriteria(Activity.class).add(Restrictions.eq("ownerId",this.getId())).list().size();
}catch (Exception e)
{
LOG.error("Error while trying to count activities: ",e);
} finally {
if (session != null) {
session.close();
}
}
return ret;
}
@Override
public void addActivityToDB(IActivity activity) {
boolean err = false;
Activity newAct = new Activity(activity);
newAct.setPublished(Long.toString(new Date().getTime())); // NOTICE THAT THE TIME IS BEING SET IN THE SERVER
LOG.info("adding activity with id: "+ this.getId());
newAct.setOwnerId(this.getId());
long actv_id = 0;
Session session = null;
Transaction t = null;
try{
session = this.getSessionFactory().openSession();
t = session.beginTransaction();
actv_id = (Long) session.save(newAct);
t.commit();
}catch(Exception e){
if (t != null) {
t.rollback();
}
LOG.error("Saving activity failed, rolling back: ",e);
err = true;
}finally{
if (session != null) {
session.close();
}
}
// Publishing TO PUBSUB
if(!err && isPubSubEnabled() && getPubSubcli() !=null){
try {
LOG.info("going to call pubsub");
getPubSubcli().publisherPublish(this.ownerCSS, this.getId(), Long.toString(actv_id), iactivToMarshActiv(newAct));
} catch (XMPPError e) {
LOG.error("XMPPError publishing activity to pubsub: ",e);
} catch (CommunicationException e) {
LOG.error("Communication error publishing activity to pubsub: ",e);
}
}
LOG.debug("done publishing activity on pubsub");
}
@Override
public void addActivity(IActivity activity, IActivityFeedCallback c) {
org.societies.api.schema.activityfeed.MarshaledActivityFeed result = new org.societies.api.schema.activityfeed.MarshaledActivityFeed();
AddActivityResponse r = new AddActivityResponse();
this.addActivityToDB(activity);
r.setResult(true); //TODO. add a return on the activity feed method
result.setAddActivityResponse(r);
LOG.debug("going to call callback from addActivity with result " + r.isResult());
c.receiveResult(result);
}
@Override
@Async
public void addActivity(IActivity activity) {
this.addActivityToDB(activity);
}
public int cleanupFeed(String criteria) {
int ret = 0;
String forever = "0 "+Long.toString(System.currentTimeMillis());
List<IActivity> toBeDeleted = getActivitiesFromDB(criteria,forever);
Session session = null;
Transaction t = null;
try{
session = getSessionFactory().openSession();
t = session.beginTransaction();
for(IActivity act : toBeDeleted){
session.delete((Activity)act);
}
t.commit();
}catch(Exception e){
if (t != null) {
t.rollback();
}
LOG.error("deleting activities failed, rolling back",e);
}finally{
if (session != null) {
session.close();
}
}
return ret;
}
@Override
public void cleanupFeed(String criteria, IActivityFeedCallback c) {
org.societies.api.schema.activityfeed.MarshaledActivityFeed result = new org.societies.api.schema.activityfeed.MarshaledActivityFeed();
CleanUpActivityFeedResponse r = new CleanUpActivityFeedResponse();
r.setResult(this.cleanupFeed(criteria));
result.setCleanUpActivityFeedResponse(r);
c.receiveResult(result);
}
public void init()
{
LOG.info("in activityfeed init");
}
public void close()
{
LOG.info("in activityfeed close");
}
public boolean deleteActivity(IActivity activity) {
if(!list.contains(activity)) {
return false;
}
boolean ret = list.remove(activity);
Session session = null;
Transaction t = null;
try{
session = getSessionFactory().openSession();
t = session.beginTransaction();
session.delete(activity);
t.commit();
}catch (Exception e){
if (t != null) {
t.rollback();
}
LOG.error("Error when trying to delete activity: ",e);
}finally {
if(session!=null) {
session.close();
}
}
return ret;
}
@Override
public void deleteActivity(IActivity activity, IActivityFeedCallback c) {
org.societies.api.schema.activityfeed.MarshaledActivityFeed result = new org.societies.api.schema.activityfeed.MarshaledActivityFeed();
DeleteActivityResponse r = new DeleteActivityResponse();
r.setResult(this.deleteActivity(activity));
result.setDeleteActivityResponse(r);
c.receiveResult(result);
}
@Override
synchronized public long importActivityEntries(List<?> activityEntries) {
long ret = 0;
if(activityEntries.size() == 0){
LOG.error("list is empty, exiting");
return ret;
}
if(!ActivityEntry.class.isInstance(activityEntries.get(0))){ //just checking the first entry.
LOG.error("first instance in the given list is not of type ActivityEntry, exiting");
return ret;
}
List<ActivityEntry> castedList = (List<ActivityEntry>) activityEntries;
Activity newAct = null;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
ParsePosition pp = new ParsePosition(0);
Session session = null;
Transaction t = null;
try{
session = getSessionFactory().openSession();
t = session.beginTransaction();
for(ActivityEntry act : castedList){
pp.setIndex(0);
newAct = new Activity();
newAct.setActor(getContentIfNotNull(act.getActor()));
newAct.setOwnerId(this.getId());
newAct.setObject(getContentIfNotNull(act.getObject()));
newAct.setPublished(Long.toString(df.parse(act.getPublished(),pp).getTime()));
newAct.setTarget(getContentIfNotNull(act.getTarget()));
newAct.setVerb(act.getVerb());
ret++;
session.save(newAct);
}
t.commit();
}catch(Exception e){
if (t != null) {
t.rollback();
}
LOG.error("Importing of activities from social data failed:",e);
}finally{
if(session!=null) {
session.close();
}
}
return ret;
}
@Override
public IActivity getEmptyIActivity(){
return new Activity();
}
//timeperiod: "millisecondssinceepoch millisecondssinceepoch+n"
//where n has to be equal to or greater than 0
public List<IActivity> getActivitiesFromDB(String timePeriod) {
LOG.info("in persisted activityfeed getActivitiesFromDB, gettin data from DB ownerID:"+this.getId());
ArrayList<IActivity> ret = new ArrayList<IActivity>();
String times[] = timePeriod.split(" ",2);
if(times.length < 2){
LOG.error("timeperiod string was malformed: "+timePeriod);
return ret;
}
long fromTime = 0;long toTime = 0;
try{
fromTime = Long.parseLong(times[0]);
toTime = Long.parseLong(times[1]);
}catch(Exception e){
LOG.error("time period string was malformed, could not parse long");
return ret;
}
Session session = null;
List<Activity> retList = null;
try{
session = this.getSessionFactory().openSession();
retList = session.createCriteria(Activity.class).add(Restrictions.gt("time", new Long(fromTime))).add(Restrictions.lt("time", new Long(toTime))).add(Restrictions.eq("ownerId",this.getId())).list();
} catch (Exception e) {
LOG.error("getting activities query failed: ",e);
} finally {
if (session != null) {
session.close();
}
}
LOG.info("time period: "+fromTime+" - " + toTime);
if(retList != null){
for(Activity act : retList){
if(Long.parseLong(act.getPublished())>=fromTime && Long.parseLong(act.getPublished())<=toTime){
act.repopHash();
ret.add(act);
}
}
}
return ret;
}
public List<IActivity> getActivitiesFromDB(String query, String timePeriod) {
ArrayList<IActivity> ret = new ArrayList<IActivity>();
List<IActivity> tmp = this.getActivitiesFromDB(timePeriod);
if(tmp.size()==0) {
LOG.error("time period did not contain any activities");
return ret;
}
//start parsing query..
JSONObject arr;
try {
arr = new JSONObject(query);
} catch (JSONException e) {
LOG.error("Error parsing JSON: ",e);
return ret;
}
LOG.info("loaded JSON");
String methodName; String filterBy; String filterValue;
try {
methodName = (new JSONArray(arr.getString("filterOp"))).getString(0);
filterBy = (new JSONArray(arr.getString("filterBy"))).getString(0);
filterValue = (new JSONArray(arr.getString("filterValue"))).getString(0);
} catch (JSONException e1) {
LOG.error("Error parsing JSON: ",e1);
return ret;
}
LOG.info("loaded JSON values");
Method method;
try {
method = ActivityString.class.getMethod(methodName, String.class);
} catch (SecurityException e) {
LOG.error("Security error getting filtering method for string");
return ret;
} catch (NoSuchMethodException e) {
LOG.error("No such filterOp: "+methodName+ " we do however have: ");
for(Method m : ActivityString.class.getMethods()){
LOG.error(m.getName());
}
return ret;
}
LOG.debug("created method");
//filter..
try {
for(IActivity act : tmp){
if((Boolean)method.invoke(((Activity)act).getValue(filterBy),filterValue) ){
ret.add(act);
}
}
} catch (IllegalArgumentException e) {
LOG.error("Illegal argument for the filterOp: ",e);
} catch (IllegalAccessException e) {
LOG.error("Illegal access for the filterOp: ",e);
} catch (InvocationTargetException e) {
LOG.error("Invocation target exception for the filterOp: ",e);
}
return ret;
}
public List<IActivity> getActivitiesFromDB(String CssId, String query,
String timePeriod) {
return this.getActivitiesFromDB(query,timePeriod);
}
@Override
public void getActivities(String query, String timePeriod,
IActivityFeedCallback c) {
LOG.debug("local get activities using query WITH CALLBACK called");
List<IActivity> iActivityList = this.getActivitiesFromDB(query,timePeriod);
org.societies.api.schema.activityfeed.MarshaledActivityFeed ac = new org.societies.api.schema.activityfeed.MarshaledActivityFeed();
GetActivitiesResponse g = new GetActivitiesResponse();
ac.setGetActivitiesResponse(g);
List<org.societies.api.schema.activity.MarshaledActivity> marshalledActivList = new ArrayList<org.societies.api.schema.activity.MarshaledActivity>();
this.iactivToMarshActvList(iActivityList, marshalledActivList);
g.setMarshaledActivity(marshalledActivList);
c.receiveResult(ac);
}
@Override
public void getActivities(String timePeriod, long n, IActivityFeedCallback c) {
LOG.debug("local get activities WITH CALLBACK called");
long nn = n;
List<IActivity> iActivityList = this.getActivitiesFromDB(timePeriod);
List<IActivity> ret = new ArrayList<IActivity>();
Collections.sort(iActivityList,new Comparator<IActivity>() {
@Override
public int compare(IActivity iActivity, IActivity iActivity1) {
return (Long.parseLong(iActivity.getPublished())>Long.parseLong(iActivity1.getPublished())) ? 1 : -1;
}
});
if(iActivityList.size()<nn) {
nn = iActivityList.size();
}
for(int i=0;i<nn;i++) {
ret.add(iActivityList.get(i));
}
org.societies.api.schema.activityfeed.MarshaledActivityFeed ac = new org.societies.api.schema.activityfeed.MarshaledActivityFeed();
GetActivitiesResponse g = new GetActivitiesResponse();
ac.setGetActivitiesResponse(g);
List<org.societies.api.schema.activity.MarshaledActivity> marshalledActivList = new ArrayList<org.societies.api.schema.activity.MarshaledActivity>();
this.iactivToMarshActvList(ret, marshalledActivList);
g.setMarshaledActivity(marshalledActivList);
c.receiveResult(ac);
}
public void getActivities(String timePeriod, IActivityFeedCallback c) {
LOG.debug("local get activities WITH CALLBACK called");
List<IActivity> iActivityList = this.getActivitiesFromDB(timePeriod);
org.societies.api.schema.activityfeed.MarshaledActivityFeed ac = new org.societies.api.schema.activityfeed.MarshaledActivityFeed();
GetActivitiesResponse g = new GetActivitiesResponse();
ac.setGetActivitiesResponse(g);
List<org.societies.api.schema.activity.MarshaledActivity> marshalledActivList = new ArrayList<org.societies.api.schema.activity.MarshaledActivity>();
this.iactivToMarshActvList(iActivityList, marshalledActivList);
g.setMarshaledActivity(marshalledActivList);
c.receiveResult(ac);
}
@Override
public void getActivities(String query, String timePeriod, long n, IActivityFeedCallback c) {
LOG.debug("local get activities WITH CALLBACK called");
List<IActivity> iActivityList = this.getActivitiesFromDB(query,timePeriod);
List<IActivity> ret = new ArrayList<IActivity>();
Collections.sort(iActivityList,new Comparator<IActivity>() {
@Override
public int compare(IActivity iActivity, IActivity iActivity1) {
return (Long.parseLong(iActivity.getPublished())>Long.parseLong(iActivity1.getPublished())) ? 1 : -1;
}
});
for(int i=0;i<n;i++) {
ret.add(iActivityList.get(i));
}
org.societies.api.schema.activityfeed.MarshaledActivityFeed ac = new org.societies.api.schema.activityfeed.MarshaledActivityFeed();
GetActivitiesResponse g = new GetActivitiesResponse();
ac.setGetActivitiesResponse(g);
List<org.societies.api.schema.activity.MarshaledActivity> marshalledActivList = new ArrayList<org.societies.api.schema.activity.MarshaledActivity>();
this.iactivToMarshActvList(ret, marshalledActivList);
g.setMarshaledActivity(marshalledActivList);
c.receiveResult(ac);
}
@Async
@Override
public Future<List<IActivity>> getActivities(String query, String timePeriod, long n) {
long nn = n;
List<IActivity> iActivityList = null;
List<IActivity> result = new ArrayList<IActivity>();
if (timePeriod == null || timePeriod.length() == 0) {
iActivityList = this.getActivitiesFromDB(query);
} else {
iActivityList = this.getActivitiesFromDB(query,timePeriod);
}
if (iActivityList != null)
{
Collections.sort(iActivityList,new Comparator<IActivity>() {
@Override
public int compare(IActivity iActivity, IActivity iActivity1) {
return (Long.parseLong(iActivity.getPublished())>Long.parseLong(iActivity1.getPublished())) ? 1 : -1;
}
});
if (nn == 0 || nn > iActivityList.size()) {
nn = iActivityList.size();
}
for(int i=0;i<nn;i++) {
result.add(iActivityList.get(i));
}
}
return new AsyncResult<List<IActivity>>(result);
}
@Async
@Override
public Future<List<IActivity>> getActivities(String timePeriod, long n) {
long nn = n;
List<IActivity> iActivityList = null;
List<IActivity> result = new ArrayList<IActivity>();
iActivityList = this.getActivitiesFromDB(timePeriod);
if (iActivityList != null) {
Collections.sort(iActivityList,new Comparator<IActivity>() {
@Override
public int compare(IActivity iActivity, IActivity iActivity1) {
return (Long.parseLong(iActivity.getPublished())>Long.parseLong(iActivity1.getPublished())) ? 1 : -1;
}
});
if (nn == 0 || nn > iActivityList.size()) {
nn = iActivityList.size();
}
for(int i=0;i<nn;i++) {
result.add(iActivityList.get(i));
}
}
return new AsyncResult<List<IActivity>>(result);
}
public void iactivToMarshActvList(List<IActivity> iActivityList, List<org.societies.api.schema.activity.MarshaledActivity> marshalledActivList){
Iterator<IActivity> it = iActivityList.iterator();
while(it.hasNext()){
IActivity element = it.next();
marshalledActivList.add(iactivToMarshActiv(element));
}
}
public String getContentIfNotNull(ActivityObject a){
if(a == null) { return null; }
if(a.getObjectType().contains("person")) {
return a.getDisplayName();
}
if(a.getObjectType().contains("note")) {
return "note";
}
if(a.getObjectType().contains("bookmark")) {
return a.getUrl();
}
return a.getContent();
}
public org.societies.api.schema.activity.MarshaledActivity iactivToMarshActiv(IActivity iActivity){
org.societies.api.schema.activity.MarshaledActivity a = new org.societies.api.schema.activity.MarshaledActivity();
a.setActor(iActivity.getActor());
a.setVerb(iActivity.getVerb());
if(iActivity.getObject()!=null && !iActivity.getObject().isEmpty() ) {
a.setObject(iActivity.getObject());
}
if(iActivity.getPublished()!=null && !iActivity.getPublished().isEmpty() ) {
a.setPublished(iActivity.getPublished());
}
if(iActivity.getTarget()!=null && !iActivity.getTarget().isEmpty() ) {
a.setTarget(iActivity.getTarget());
}
return a;
}
public void clear(){
Session session = null;
Transaction t = null;
try{
session = getSessionFactory().openSession();
t = session.beginTransaction();
List<Activity> l = session.createCriteria(Activity.class).list();
for(Activity a : l)
{
session.delete(a);
}
t.commit();
} catch (Exception e) {
if (t != null) {
t.rollback();
}
LOG.warn("clear of activities failed:",e);
} finally {
if(session!=null) {
session.close();
}
}
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public PubsubClient getPubSubcli() {
return pubSubcli;
}
public void setPubSubcli(PubsubClient pubSubcli) {
this.pubSubcli = pubSubcli;
}
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public boolean isPubSubEnabled() {
return pubSubEnabled;
}
public void setPubSubEnabled(boolean pubSubEnabled) {
this.pubSubEnabled = pubSubEnabled;
}
}