package notification; import java.util.Set; import javax.jcr.ItemNotFoundException; import javax.jcr.RepositoryException; import javax.jcr.Session; import models.Flag; import models.Notification; import models.NotificationDAO; import models.User; import org.apache.commons.lang3.StringUtils; import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.core.id.NodeId; import org.jcrom.Jcrom; import play.Logger; import play.api.templates.Html; import play.libs.F; import service.EventManager.Event; import service.EventManager.EventReceiver; import service.EventManager.EventReceiverMessage; import service.JcrSessionFactory; import service.OrderedEvent; import service.filestore.FileStore; import service.filestore.FlagStore; import akka.actor.TypedActor; import com.google.common.collect.Sets; import com.google.inject.Inject; public class NotifierImpl implements Notifier, TypedActor.PreStart { private final JcrSessionFactory sessionFactory; private final FileStore fileStore; private final FlagStore flagStore; private final Jcrom jcrom; @Inject public NotifierImpl(JcrSessionFactory sessionFactory, FileStore fileStore, FlagStore flagStore, Jcrom jcrom) { this.sessionFactory = sessionFactory; this.fileStore = fileStore; this.flagStore = flagStore; this.jcrom = jcrom; } @Override public void preStart() { attachEventReceiver(); } protected void attachEventReceiver() { final Notifier n = TypedActor.<Notifier>self(); final EventReceiver er = new EventReceiver() { @Override public void end() {} @Override public void end(Throwable e) {} @Override public void push(OrderedEvent oe) { // Skip out-of-date messages if (oe.event().type.equals("outofdate")) return; // Trigger notification for event n.handleEvent(oe); } }; fileStore.getEventManager().tell(EventReceiverMessage.add(er, null)); } @Override public void handleEvent(final OrderedEvent oe) { if (isNotificationEvent(oe.event())) { // Notification events do not produce notifications! ;-) return; } sessionFactory.inSession(new F.Function<Session, Session>() { @Override public Session apply(Session session) throws RepositoryException { processEvent(session, oe.event()); return session; } }); } private static boolean isNotificationEvent(final Event event) { return event.type.startsWith("notification:"); } private static boolean isFlagEvent(final Event event) { return event.type.startsWith("flag:"); } private static boolean isCommentEvent(final Event event) { return event.type.startsWith("comment:"); } private void processEvent(Session session, Event event) throws RepositoryException { if (isFlagEvent(event)) { if (isEditFlag(session, event)) { sendEditNotification(session, getWatchUsers(session, event.info("target:id"), event), event); } } else if (isCommentEvent(event)) { sendCommentNotification(session, getWatchUsers(session, event.info("target:id"), event), event); } else { sendNotification(session, event); } } private Iterable<User> getWatchUsers(Session session, String nodeId, Event event) { final Set<User> users = Sets.newHashSet(); FlagStore.Manager manager = flagStore.getManager(session); for (Flag flag : manager.getFlags(FlagStore.FlagType.WATCH)) { // filter out user who triggered the event, issue #144 if(isEventOwner(flag.getUser(), event)) { continue; } if (flag.getTargetId().equals(nodeId)) { users.add(flag.getUser()); } else { try { if (((SessionImpl) session).getHierarchyManager().isAncestor( new NodeId(flag.getTargetId()), new NodeId(nodeId))) { users.add(flag.getUser()); } } catch (RepositoryException e) { // Dump trace and ignore Logger.trace(String.format("couldn't determine if %s is ancestor of %s", flag.getTargetId(), nodeId), e); } } } return users; } private boolean isEventOwner(User user, Event event) { return user!=null?StringUtils.equals(user.getId(), getEventOwnerId(event)):false; } private String getEventOwnerId(Event event) { String instigator = event.info("instigator:id"); String author = event.info("author:id"); if(instigator != null) { return instigator; } else if(author != null) { return author; } else { return event.info("owner:id"); } } private void sendNotification(Session session, final Event event) throws RepositoryException { final FileStore.Manager manager = fileStore.getManager(session); for (final User user : getWatchUsers(session, event.info("id"), event)) { FileStore.FileOrFolder item = getItem(manager, event); final String message = views.html.notification.notification.render(event, item).toString(); sendNotification(session, user, message); } } private FileStore.FileOrFolder getItem( FileStore.Manager manager, Event event) throws RepositoryException { return manager.getByIdentifier(event.info("id")); } private boolean isEditFlag(Session session, Event event) { FlagStore.FlagType t = FlagStore.FlagType.valueOf(event.info("type")); return t == FlagStore.FlagType.EDIT; } private void sendEditNotification(final Session session, final Iterable<User> users, final Event event) throws RepositoryException { final FileStore.Manager manager = fileStore.getManager(session); final FileStore.FileOrFolder fof = manager.getByIdentifier(event.info("target:id")); final Html msg; if (event.type.endsWith("create")) { msg = views.html.notification.editFlagCreated.render(event, fof); } else if (event.type.endsWith("delete")) { msg = views.html.notification.editFlagRemoved.render(event, fof); } else { return; } for (User u : users) { sendNotification(session, u, msg.toString()); } } private void sendCommentNotification(final Session session, final Iterable<User> users, final Event event) throws RepositoryException { final FileStore.Manager manager = fileStore.getManager(session); final FileStore.FileOrFolder fof = manager.getByIdentifier(event.info("target:id")); if (fof == null) { // We can't issue a useful comment notification return; } final Html msg = views.html.notification.comment.render(event, fof); for (User u : users) { sendNotification(session, u, msg.toString()); } } private void sendNotification(Session session, User to, String msg) throws ItemNotFoundException, RepositoryException { NotificationDAO notificationDao = new NotificationDAO(session, jcrom); Notification notification = new Notification(to, msg); notificationDao.create(notification); session.save(); fileStore.getEventManager().tell(Events.create(notification)); } }