org.sakaiproject.portal.service.BullhornServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.portal.service.BullhornServiceImpl.java

Source

/**
 * Copyright (c) 2003-2017 The Apereo Foundation
 *
 * Licensed under the Educational Community 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://opensource.org/licenses/ecl2
 *
 * 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.sakaiproject.portal.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.function.Function;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.Resource;
import javax.inject.Inject;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.sakaiproject.authz.api.SecurityService;
import org.sakaiproject.component.api.ServerConfigurationService;
import org.sakaiproject.event.api.Event;
import org.sakaiproject.event.api.EventTrackingService;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.lessonbuildertool.SimplePage;
import org.sakaiproject.lessonbuildertool.SimplePageComment;
import org.sakaiproject.lessonbuildertool.api.LessonBuilderEvents;
import org.sakaiproject.lessonbuildertool.model.SimplePageToolDao;
import org.sakaiproject.memory.api.Cache;
import org.sakaiproject.memory.api.MemoryService;
import org.sakaiproject.portal.api.BullhornData;
import org.sakaiproject.portal.api.BullhornHandler;
import org.sakaiproject.portal.api.BullhornService;
import org.sakaiproject.portal.beans.BullhornAlert;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.user.api.User;
import org.sakaiproject.user.api.UserDirectoryService;
import org.sakaiproject.user.api.UserNotDefinedException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BullhornServiceImpl implements BullhornService, Observer {

    private static final List<String> HANDLED_EVENTS = new ArrayList<>();

    @Inject
    private EventTrackingService eventTrackingService;
    @Inject
    private MemoryService memoryService;
    @Resource(name = "org.sakaiproject.springframework.orm.hibernate.GlobalTransactionManager")
    private PlatformTransactionManager transactionManager;
    @Inject
    private UserDirectoryService userDirectoryService;
    @Inject
    private SecurityService securityService;
    @Inject
    private ServerConfigurationService serverConfigurationService;
    @Resource(name = "org.sakaiproject.lessonbuildertool.model.SimplePageToolDao")
    private SimplePageToolDao simplePageToolDao;
    @Inject
    private SiteService siteService;
    @Resource(name = "org.sakaiproject.springframework.orm.hibernate.GlobalSessionFactory")
    private SessionFactory sessionFactory;

    private Cache<String, Map> countCache = null;

    @Autowired
    private List<BullhornHandler> handlers;

    private Map<String, BullhornHandler> handlerMap = new HashMap<>();

    public void init() {

        if (serverConfigurationService.getBoolean("portal.bullhorns.enabled", true)) {
            HANDLED_EVENTS.add(LessonBuilderEvents.COMMENT_CREATE);
            HANDLED_EVENTS.add(SiteService.EVENT_SITE_PUBLISH);

            handlers.forEach(h -> {
                h.getHandledEvents().forEach(he -> {
                    HANDLED_EVENTS.add(he);
                    handlerMap.put(he, h);
                });
            });

            eventTrackingService.addLocalObserver(this);
        }

        countCache = memoryService.getCache("bullhorn_alert_count_cache");
    }

    public void update(Observable o, final Object arg) {

        if (arg instanceof Event) {
            Event e = (Event) arg;
            String event = e.getEvent();
            // We add this comparation with UNKNOWN_USER because implementation of BaseEventTrackingService
            // UNKNOWN_USER is an user in a server without session. 
            if (HANDLED_EVENTS.contains(event) && !EventTrackingService.UNKNOWN_USER.equals(e.getUserId())) {
                String ref = e.getResource();
                String context = e.getContext();
                String[] pathParts = ref.split("/");
                String from = e.getUserId();
                long at = e.getEventTime().getTime();
                try {
                    BullhornHandler handler = handlerMap.get(event);

                    if (handler != null) {
                        Optional<List<BullhornData>> result = handler.handleEvent(e, countCache);
                        if (result.isPresent()) {
                            result.get().forEach(bd -> {

                                if (bd.isSocial()) {
                                    doSocialInsert(bd.getFrom(), bd.getTo(), event, ref, e.getEventTime(),
                                            bd.getUrl());
                                } else {
                                    doAcademicInsert(from, bd.getTo(), event, ref, bd.getTitle(), bd.getSiteId(),
                                            e.getEventTime(), bd.getUrl());
                                }
                            });
                        }
                    } else if (LessonBuilderEvents.COMMENT_CREATE.equals(event)) {
                        try {
                            long commentId = Long.parseLong(pathParts[pathParts.length - 1]);
                            SimplePageComment comment = simplePageToolDao.findCommentById(commentId);

                            String url = simplePageToolDao.getPageUrl(comment.getPageId());

                            if (url != null) {
                                List<String> done = new ArrayList<>();
                                // Alert tutor types.
                                List<User> receivers = securityService.unlockUsers(
                                        SimplePage.PERMISSION_LESSONBUILDER_UPDATE, "/site/" + context);
                                for (User receiver : receivers) {
                                    String to = receiver.getId();
                                    if (!to.equals(from)) {
                                        doAcademicInsert(from, to, event, ref, "title", context, e.getEventTime(),
                                                url);
                                        done.add(to);
                                        countCache.remove(to);
                                    }
                                }

                                // Get all the comments in the same item
                                List<SimplePageComment> comments = simplePageToolDao
                                        .findCommentsOnItems(Arrays.asList(new Long[] { comment.getItemId() }));

                                if (comments.size() > 1) {
                                    // Not the first, alert all the other commenters unless they already have been
                                    for (SimplePageComment c : comments) {
                                        String to = c.getAuthor();
                                        if (!to.equals(from) && !done.contains(to)) {
                                            doAcademicInsert(from, to, event, ref, "title", context,
                                                    e.getEventTime(), url);
                                            done.add(to);
                                            countCache.remove(to);
                                        }
                                    }
                                }
                            } else {
                                log.error("null url for page {}", comment.getPageId());
                            }
                        } catch (NumberFormatException nfe) {
                            log.error("Caught number format exception whilst handling events", nfe);
                        }
                    } else if (SiteService.EVENT_SITE_PUBLISH.equals(event)) {
                        final String siteId = pathParts[2];

                        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);

                        transactionTemplate.execute(new TransactionCallbackWithoutResult() {

                            protected void doInTransactionWithoutResult(TransactionStatus status) {

                                final List<BullhornAlert> deferredAlerts = sessionFactory.getCurrentSession()
                                        .createCriteria(BullhornAlert.class).add(Restrictions.eq("deferred", true))
                                        .add(Restrictions.eq("siteId", siteId)).list();

                                for (BullhornAlert da : deferredAlerts) {
                                    da.setDeferred(false);
                                    sessionFactory.getCurrentSession().update(da);
                                    countCache.remove(da.getToUser());
                                }
                            }
                        });
                    }
                } catch (Exception ex) {
                    log.error("Caught exception whilst handling events", ex);
                }
            }
        }
    }

    private void doAcademicInsert(String from, String to, String event, String ref, String title, String siteId,
            Date eventDate, String url) {

        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {

            protected void doInTransactionWithoutResult(TransactionStatus status) {

                BullhornAlert ba = new BullhornAlert();
                ba.setAlertType(ACADEMIC);
                ba.setFromUser(from);
                ba.setToUser(to);
                ba.setEvent(event);
                ba.setRef(ref);
                ba.setTitle(title);
                ba.setSiteId(siteId);
                ba.setEventDate(eventDate.toInstant());
                ba.setUrl(url);
                try {
                    ba.setDeferred(!siteService.getSite(siteId).isPublished());
                } catch (IdUnusedException iue) {
                    log.warn("Failed to find site with id {} while setting deferred to published", siteId);
                }

                sessionFactory.getCurrentSession().persist(ba);
            }
        });
    }

    private void doSocialInsert(String from, String to, String event, String ref, Date eventDate, String url) {

        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {

            protected void doInTransactionWithoutResult(TransactionStatus status) {

                BullhornAlert ba = new BullhornAlert();
                ba.setAlertType(SOCIAL);
                ba.setFromUser(from);
                ba.setToUser(to);
                ba.setEvent(event);
                ba.setRef(ref);
                ba.setTitle("");
                ba.setSiteId("");
                ba.setEventDate(eventDate.toInstant());
                ba.setUrl(url);
                ba.setDeferred(false);

                sessionFactory.getCurrentSession().persist(ba);
            }
        });
    }

    @Transactional
    public List<BullhornAlert> getSocialAlerts(String userId) {

        List<BullhornAlert> alerts = sessionFactory.getCurrentSession().createCriteria(BullhornAlert.class)
                .add(Restrictions.eq("alertType", SOCIAL)).add(Restrictions.eq("toUser", userId)).list();

        for (BullhornAlert alert : alerts) {
            try {
                User fromUser = userDirectoryService.getUser(alert.getFromUser());
                alert.setFromDisplayName(fromUser.getDisplayName());
            } catch (UserNotDefinedException unde) {
                alert.setFromDisplayName(alert.getFromUser());
            }
        }

        return alerts;
    }

    @Transactional
    public long getSocialAlertCount(String userId) {

        Map<String, Long> cachedCounts = (Map<String, Long>) countCache.get(userId);

        if (cachedCounts == null) {
            cachedCounts = new HashMap<>();
        }

        Long count = cachedCounts.get("social");

        if (count != null) {
            log.debug("bullhorn_alert_count_cache hit");
            return count;
        } else {
            log.debug("bullhorn_alert_count_cache miss");

            count = (Long) sessionFactory.getCurrentSession().createCriteria(BullhornAlert.class)
                    .add(Restrictions.eq("alertType", SOCIAL)).add(Restrictions.eq("toUser", userId))
                    .setProjection(Projections.rowCount()).uniqueResult();

            cachedCounts.put("social", count);
            countCache.put(userId, cachedCounts);
            return count;
        }
    }

    @Transactional
    public boolean clearBullhornAlert(String userId, long alertId) {

        sessionFactory.getCurrentSession().createQuery("delete BullhornAlert where id = :id and toUser = :toUser")
                .setLong("id", alertId).setString("toUser", userId).executeUpdate();
        countCache.remove(userId);
        return true;
    }

    @Transactional
    public List<BullhornAlert> getAcademicAlerts(String userId) {

        List<BullhornAlert> alerts = sessionFactory.getCurrentSession().createCriteria(BullhornAlert.class)
                .add(Restrictions.eq("alertType", ACADEMIC)).add(Restrictions.eq("deferred", false))
                .add(Restrictions.eq("toUser", userId)).list();

        for (BullhornAlert alert : alerts) {
            try {
                User fromUser = userDirectoryService.getUser(alert.getFromUser());
                alert.setFromDisplayName(fromUser.getDisplayName());
                if (StringUtils.isNotBlank(alert.getSiteId())) {
                    alert.setSiteTitle(siteService.getSite(alert.getSiteId()).getTitle());
                }
            } catch (UserNotDefinedException unde) {
                alert.setFromDisplayName(alert.getFromUser());
            } catch (IdUnusedException iue) {
                alert.setSiteTitle(alert.getSiteId());
            }
        }

        return alerts;
    }

    @Transactional
    public long getAcademicAlertCount(String userId) {

        Map<String, Long> cachedCounts = (Map<String, Long>) countCache.get(userId);

        if (cachedCounts == null) {
            cachedCounts = new HashMap<>();
        }

        Long count = cachedCounts.get("academic");

        if (count != null) {
            log.debug("bullhorn_alert_count_cache hit");
            return count;
        } else {
            log.debug("bullhorn_alert_count_cache miss");

            count = (Long) sessionFactory.getCurrentSession().createCriteria(BullhornAlert.class)
                    .add(Restrictions.eq("alertType", ACADEMIC)).add(Restrictions.eq("toUser", userId))
                    .add(Restrictions.eq("deferred", false)).setProjection(Projections.rowCount()).uniqueResult();
            cachedCounts.put("academic", count);
            countCache.put(userId, cachedCounts);
            return count;
        }
    }

    @Transactional
    public boolean clearAllSocialAlerts(String userId) {
        return clearAllAlerts(SOCIAL, userId);
    }

    @Transactional
    public boolean clearAllAcademicAlerts(String userId) {
        return clearAllAlerts(ACADEMIC, userId);
    }

    private boolean clearAllAlerts(String alertType, String userId) {

        sessionFactory.getCurrentSession().createQuery(
                "delete BullhornAlert where alertType = :alertType and toUser = :toUser and deferred = :deferred")
                .setString("alertType", alertType).setString("toUser", userId).setBoolean("deferred", false)
                .executeUpdate();
        countCache.remove(userId);
        return true;
    }
}