Package org.onesocialweb.openfire.manager

Source Code of org.onesocialweb.openfire.manager.ActivityManager

/*
*  Copyright 2010 Vodafone Group Services Ltd.
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*      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.onesocialweb.openfire.manager;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import javax.activity.InvalidActivityException;
import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.dom4j.dom.DOMDocument;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.roster.Roster;
import org.jivesoftware.openfire.roster.RosterItem;
import org.jivesoftware.openfire.roster.RosterManager;
import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.onesocialweb.model.acl.AclAction;
import org.onesocialweb.model.acl.AclFactory;
import org.onesocialweb.model.acl.AclRule;
import org.onesocialweb.model.acl.AclSubject;
import org.onesocialweb.model.activity.ActivityActor;
import org.onesocialweb.model.activity.ActivityEntry;
import org.onesocialweb.model.activity.ActivityFactory;
import org.onesocialweb.model.activity.ActivityObject;
import org.onesocialweb.model.atom.AtomReplyTo;
import org.onesocialweb.model.atom.DefaultAtomHelper;
import org.onesocialweb.openfire.OswPlugin;
import org.onesocialweb.openfire.exception.AccessDeniedException;
import org.onesocialweb.openfire.handler.activity.PEPActivityHandler;
import org.onesocialweb.openfire.model.ActivityMessage;
import org.onesocialweb.openfire.model.PersistentActivityMessage;
import org.onesocialweb.openfire.model.PersistentSubscription;
import org.onesocialweb.openfire.model.Subscription;
import org.onesocialweb.openfire.model.acl.PersistentAclFactory;
import org.onesocialweb.openfire.model.activity.PersistentActivityEntry;
import org.onesocialweb.openfire.model.activity.PersistentActivityFactory;
import org.onesocialweb.xml.dom.ActivityDomWriter;
import org.onesocialweb.xml.dom.imp.DefaultActivityDomWriter;
import org.onesocialweb.xml.namespace.Atom;
import org.w3c.dom.Element;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;

/**
* The activity manager is a singleton class taking care of all the business
* logic related to querying, creating, updating and deleting activities.
*
* @author eschenal
*
*/
public class ActivityManager {

  /**
   * Singleton: keep a static reference to teh only instance
   */
  private static ActivityManager instance;

  public static ActivityManager getInstance() {
    if (instance == null) {
      // Carefull, we are in a threaded environment !
      synchronized (ActivityManager.class) {
        instance = new ActivityManager();
      }
    }
    return instance;
  }

  /**
   * Class dependencies
   * TODO Make this a true dependency injection
   */
  private final ActivityFactory activityFactory;

  private final AclFactory aclFactory;

  /**
   * Publish a new activity to the activity stream of the given user.
   * activity-actor element is overwrittern using the user profile data to
   * avoid spoofing. Notifications messages are sent to the users subscribed
   * to this user activities.
   *
   * @param user
   *            The user who the activity belongs to
   * @param entry
   *            The activity entry to publish
   * @throws UserNotFoundException
   */
  public void publishActivity(String userJID, ActivityEntry entry) throws UserNotFoundException {
    // Overide the actor to avoid spoofing
    User user = UserManager.getInstance().getUser(new JID(userJID).getNode());
    ActivityActor actor = activityFactory.actor();
    actor.setUri(userJID);
    actor.setName(user.getName());
    actor.setEmail(user.getEmail());

    // Persist the activities
    final EntityManager em = OswPlugin.getEmFactory().createEntityManager();
    em.getTransaction().begin();
    entry.setId(DefaultAtomHelper.generateId());
    for (ActivityObject object : entry.getObjects()) {
      object.setId(DefaultAtomHelper.generateId());
    }
    entry.setActor(actor);
    entry.setPublished(Calendar.getInstance().getTime());
    em.persist(entry);
    em.getTransaction().commit();
    em.close();

    // Broadcast the notifications
    notify(userJID, entry);
  }
 
  /**
   * Updates an activity in the activity stream of the given user.
   * activity-actor element is overwrittern using the user profile data to
   * avoid spoofing. Notifications messages are sent to the users subscribed
   * to this user activities.
   *
   * @param user
   *            The user who the activity belongs to
   * @param entry
   *            The activity entry to update
   * @throws UserNotFoundException
   */
  public void updateActivity(String userJID, ActivityEntry entry) throws UserNotFoundException, UnauthorizedException {
    // Overide the actor to avoid spoofing
    User user = UserManager.getInstance().getUser(new JID(userJID).getNode());
    ActivityActor actor = activityFactory.actor();
    actor.setUri(userJID);
    actor.setName(user.getName());
    actor.setEmail(user.getEmail());
   
    // Persist the activities
    final EntityManager em = OswPlugin.getEmFactory().createEntityManager();
    em.getTransaction().begin();
    PersistentActivityEntry oldEntry=em.find(PersistentActivityEntry.class, entry.getId());
   
    if ((oldEntry==null) || (!oldEntry.getActor().getUri().equalsIgnoreCase(userJID)))
      throw new UnauthorizedException();
   
   
    Date published=oldEntry.getPublished();
    entry.setPublished(published);
    entry.setUpdated(Calendar.getInstance().getTime());
   
    em.remove(oldEntry);
   
    entry.setActor(actor);
    em.persist(entry);
    em.getTransaction().commit();
    em.close();

    // Broadcast the notifications
    notify(userJID, entry);
  }
 
  public void deleteActivity(String fromJID, String activityId) throws UnauthorizedException {
   
    final EntityManager em = OswPlugin.getEmFactory().createEntityManager();
    em.getTransaction().begin();
   
    PersistentActivityEntry activity= em.find(PersistentActivityEntry.class, activityId);
   
    if ((activity==null) || (!activity.getActor().getUri().equalsIgnoreCase(fromJID)))
      throw new UnauthorizedException();
   
    em.remove(activity);
   
    em.getTransaction().commit();
    em.close();
   
    notifyDelete(fromJID, activityId);
  }
 
  public void deleteMessage(String activityId)  {
   
    final EntityManager em = OswPlugin.getEmFactory().createEntityManager();
    em.getTransaction().begin();
   
    Query query = em.createQuery("SELECT x FROM Messages x WHERE x.activity.id = ?1");
    query.setParameter(1, activityId);   
    List<ActivityMessage> messages = query.getResultList();
    for (ActivityMessage message:messages){
      em.remove(message);
    }

    em.getTransaction().commit();
    em.close();

  }

  /**
   * Retrieve the last activities of the target user, taking into account the
   * access rights of the requesting user.
   *
   * @param requestorJID
   *            the user requesting the activities
   * @param targetJID
   *            the user whose activities are requested
   * @return an immutable list of the last activities of the target entity that can be seen by the
   *         requesting entity
   * @throws UserNotFoundException
   */
  @SuppressWarnings("unchecked")
  public List<ActivityEntry> getActivities(String requestorJID, String targetJID) throws UserNotFoundException {
    final EntityManager em = OswPlugin.getEmFactory().createEntityManager();
    Query query = em.createQuery("SELECT DISTINCT entry FROM ActivityEntry entry" + "             JOIN entry.rules rule "
        + "             JOIN rule.actions action " + "             JOIN rule.subjects subject "
        + "             WHERE entry.actor.uri = :target " + "             AND action.name = :view "
        + "             AND action.permission = :grant " + "             AND (subject.type = :everyone "
        + "                  OR (subject.type = :group_type " + "                     AND subject.name IN (:groups)) "
        + "                  OR (subject.type = :person " + "                      AND subject.name = :jid)) ORDER BY entry.published DESC");

    // Parametrize the query
    query.setParameter("target", targetJID);
    query.setParameter("view", AclAction.ACTION_VIEW);
    query.setParameter("grant", AclAction.PERMISSION_GRANT);
    query.setParameter("everyone", AclSubject.EVERYONE);
    query.setParameter("group_type", AclSubject.GROUP);
    query.setParameter("groups", getGroups(targetJID, requestorJID));
    query.setParameter("person", AclSubject.PERSON);
    query.setParameter("jid", requestorJID);
    query.setMaxResults(20);
    List<ActivityEntry> result = query.getResultList();
    em.close();

    return Collections.unmodifiableList(result);
  }

  /**
   * Handle an activity pubsub event. Such a message is usually
   * received by a user in these conditions: - the local user has subscribed
   * to the remote user activities - the local user is "mentionned" in this
   * activity - this activity relates to another activity of the local user
   *
   * @param remoteJID
   *            the entity sending the message
   * @param localJID
   *            the entity having received the message
   * @param activity
   *            the activity contained in the message
   * @throws InvalidActivityException
   * @throws AccessDeniedException
   */
  public synchronized void handleMessage(String remoteJID, String localJID, ActivityEntry activity) throws InvalidActivityException, AccessDeniedException {

    // Validate the activity
    if (activity == null || !activity.hasId()) {
      throw new InvalidActivityException();
    }
   
    // Create a message for the recipient
    ActivityMessage message = new PersistentActivityMessage();
    message.setSender(remoteJID);
    message.setRecipient(localJID);
    //in case of an activity update we keep the received date as the date of
    //original publish, otherwise the updated posts will start showing on top of
    // the inbox..which we agreed we don't want...
    message.setReceived(activity.getPublished());

    // Search if the activity exists in the database
    final EntityManager em = OswPlugin.getEmFactory().createEntityManager();
    PersistentActivityEntry previousActivity = em.find(PersistentActivityEntry.class, activity.getId());

    // Assign the activity to the existing one if it exists
    if (previousActivity != null) {
      message.setActivity(previousActivity);
    } else {
      message.setActivity(activity);
    }
   
    //in case of an update the message will already exist in the DB
    Query query = em.createQuery("SELECT x FROM Messages x WHERE x.activity.id = ?1");
    query.setParameter(1, activity.getId());   
    List<ActivityMessage> messages = query.getResultList();
   
    em.getTransaction().begin();
    for (ActivityMessage oldMessage:messages){
      if (oldMessage.getRecipient().equalsIgnoreCase(localJID))
        em.remove(oldMessage);
    }
    em.getTransaction().commit();

    // We go ahead and post the message to the recipient mailbox
    em.getTransaction().begin();
    em.persist(message);
    em.getTransaction().commit();
    em.close();
       
  }
 
 
 
  /**
   * Subscribe an entity to another entity activities.
   *
   * @param from the subscriber
   * @param to entity being subscribed to
   * @throws AlreadySubscribed
   */
  @SuppressWarnings("unchecked")
  public synchronized void subscribe(String from, String to) {
   
    // Check if it already exist
    final EntityManager em = OswPlugin.getEmFactory().createEntityManager();
    Query query = em.createQuery("SELECT x FROM Subscriptions x WHERE x.subscriber = ?1 AND x.target = ?2");
    query.setParameter(1, from);
    query.setParameter(2, to);
    List<Subscription> subscriptions = query.getResultList();
   
    // If already exist, we don't have anything left to do
    if (subscriptions != null && subscriptions.size() > 0) {
      em.close();
      return;
    }
   
    // Add the subscription
    Subscription subscription = new PersistentSubscription();
    subscription.setSubscriber(from);
    subscription.setTarget(to);
    subscription.setCreated(Calendar.getInstance().getTime());
   
    // Store
    em.getTransaction().begin();
    em.persist(subscription);
    em.getTransaction().commit();
    em.close();
  }
 

  /**
   * Delete a subscription.
   *
   * @param from the entity requesting to unsubscribe
   * @param to the subscription target
   * @throws SubscriptionNotFound
   */
  @SuppressWarnings("unchecked")
  public synchronized void unsubscribe(String from, String to) {
    EntityManager em  = OswPlugin.getEmFactory().createEntityManager();
   
    // Check if it already exist
    Query query = em.createQuery("SELECT x FROM Subscriptions x WHERE x.subscriber = ?1 AND x.target = ?2");
    query.setParameter(1, from);
    query.setParameter(2, to);
    List<Subscription> subscriptions = query.getResultList();
   
    // If it does not exist, we don't have anything left to do
    if (subscriptions == null || subscriptions.size()== 0) {
      em.close();
      return;
    }
   
    // Remove the subscriptions
    // There should never be more than one.. but better safe than sorry
    em.getTransaction().begin();
    for (Subscription activitySubscription : subscriptions) {
      em.remove(activitySubscription);
    }
    em.getTransaction().commit();
    em.close();
  }
 
  @SuppressWarnings("unchecked")
  public List<Subscription> getSubscribers(String targetJID) {
    // Get a list of people who are interested by this stuff
    final EntityManager em = OswPlugin.getEmFactory().createEntityManager();
    Query query = em.createQuery("SELECT x FROM Subscriptions x WHERE x.target = ?1");
    query.setParameter(1, targetJID);
    List<Subscription> subscriptions = query.getResultList();
    em.close();
    return subscriptions;
  }
 
  @SuppressWarnings("unchecked")
  public List<Subscription> getSubscriptions(String subscriberJID) {
    // Get a list of people who are interested by this stuff
    final EntityManager em = OswPlugin.getEmFactory().createEntityManager();
    Query query = em.createQuery("SELECT x FROM Subscriptions x WHERE x.subscriber = ?1");
    query.setParameter(1, subscriberJID);
    List<Subscription> subscriptions = query.getResultList();
    em.close();
    return subscriptions;
  }

  private void notify(String fromJID, ActivityEntry entry) throws UserNotFoundException {

    // TODO We may want to do some cleaning of activities before
    // forwarding them (e.g. remove the acl, it is no one business)
    final ActivityDomWriter writer = new DefaultActivityDomWriter();
    final XMPPServer server = XMPPServer.getInstance();
    final List<Subscription> subscriptions = getSubscribers(fromJID);
  //  final Roster roster = XMPPServer.getInstance().getRosterManager().getRoster(new JID(fromJID).getNode());
    final DOMDocument domDocument = new DOMDocument();

    // Prepare the message
    final Element entryElement = (Element) domDocument.appendChild(domDocument.createElementNS(Atom.NAMESPACE, Atom.ENTRY_ELEMENT));
    writer.write(entry, entryElement);
    domDocument.removeChild(entryElement);

    final Message message = new Message();
    message.setFrom(fromJID);
    message.setBody("New activity: " + entry.getTitle());
    message.setType(Message.Type.headline);
    org.dom4j.Element eventElement = message.addChildElement("event", "http://jabber.org/protocol/pubsub#event");
    org.dom4j.Element itemsElement = eventElement.addElement("items");
    itemsElement.addAttribute("node", PEPActivityHandler.NODE);
    org.dom4j.Element itemElement = itemsElement.addElement("item");
    itemElement.addAttribute("id", entry.getId());
    itemElement.add((org.dom4j.Element) entryElement);

    // Keep a list of people we sent it to avoid duplicates
    List<String> alreadySent = new ArrayList<String>();
   
    // Send to this user
    alreadySent.add(fromJID);
    message.setTo(fromJID);
    server.getMessageRouter().route(message)
           
    // Send to all subscribers
    for (Subscription activitySubscription : subscriptions) {
      String recipientJID = activitySubscription.getSubscriber();
      if (!canSee(fromJID, entry, recipientJID)) {
        continue;
      }
      alreadySent.add(recipientJID);           
      message.setTo(recipientJID);
      server.getMessageRouter().route(message)
    }

    // Send to recipients, if they can see it and have not already received it
    if (entry.hasRecipients()) {
      for (AtomReplyTo recipient : entry.getRecipients()) {
        //TODO This is dirty, the recipient should be an IRI etc...
        String recipientJID = recipient.getHref()
        if (!alreadySent.contains(recipientJID) && canSee(fromJID, entry, recipientJID)) {
          alreadySent.add(fromJID);
         
          message.setTo(recipientJID);
          server.getMessageRouter().route(message);                       
        }
      }
    }     
  }
 

  private void notifyDelete(String fromJID, String activityId) {

   
    final XMPPServer server = XMPPServer.getInstance();
    final List<Subscription> subscriptions = getSubscribers(fromJID);
 
    // Prepare the message
   
    final Message message = new Message();
    message.setFrom(fromJID);
    message.setBody("Delete activity: " + activityId);
    message.setType(Message.Type.headline);
   
    org.dom4j.Element eventElement = message.addChildElement("event", "http://jabber.org/protocol/pubsub#event");
    org.dom4j.Element itemsElement = eventElement.addElement("items");
    itemsElement.addAttribute("node", PEPActivityHandler.NODE);
    org.dom4j.Element retractElement = itemsElement.addElement("retract");
    retractElement.addAttribute("id", activityId);
   

    // Keep a list of people we sent it to avoid duplicates
    List<String> alreadySent = new ArrayList<String>();
   
    // Send to this user
    alreadySent.add(fromJID);
    message.setTo(fromJID);
    server.getMessageRouter().route(message)
           
    // Send to all subscribers
    for (Subscription activitySubscription : subscriptions) {
      String recipientJID = activitySubscription.getSubscriber();     
      alreadySent.add(recipientJID);           
      message.setTo(recipientJID);
      server.getMessageRouter().route(message)
    }     
   
  }
 
  private List<String> getGroups(String ownerJID, String userJID) {
    RosterManager rosterManager = XMPPServer.getInstance().getRosterManager();
    Roster roster;
    try {
      roster = rosterManager.getRoster(new JID(ownerJID).getNode());
      RosterItem rosterItem = roster.getRosterItem(new JID(userJID));
      if (rosterItem != null) {
        return rosterItem.getGroups();
      }
    } catch (UserNotFoundException e) {
    }

    return new ArrayList<String>();
  }

  private boolean canSee(String fromJID, ActivityEntry entry, String viewer) throws UserNotFoundException  {
    // Get a view action
    final AclAction viewAction = aclFactory.aclAction(AclAction.ACTION_VIEW, AclAction.PERMISSION_GRANT);
    AclRule rule = null;
    for (AclRule aclRule : entry.getAclRules()) {
      if (aclRule.hasAction(viewAction)) {
        rule = aclRule;
        break;
      }
    }

    // If no view action was found, we consider it is denied
    if (rule == null)
      return false;
   
    return AclManager.canSee(fromJID, rule, viewer);
   
  }

  /**
   * Private constructor to enforce the singleton
   */
  private ActivityManager() {
    activityFactory = new PersistentActivityFactory();
    aclFactory = new PersistentAclFactory();
  }
}
TOP

Related Classes of org.onesocialweb.openfire.manager.ActivityManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.