net.jforum.core.support.hibernate.CacheEvictionRules.java Source code

Java tutorial

Introduction

Here is the source code for net.jforum.core.support.hibernate.CacheEvictionRules.java

Source

/*
 * Copyright (c) JForum Team. All rights reserved.
 *
 * The software in this package is published under the terms of the LGPL
 * license a copy of which has been included with this distribution in the
 * license.txt file.
 *
 * The JForum Project
 * http://www.jforum.net
 */
package net.jforum.core.support.hibernate;

import java.util.List;

import net.jforum.entities.Attachment;
import net.jforum.entities.Forum;
import net.jforum.entities.ModerationLog;
import net.jforum.entities.PollOption;
import net.jforum.entities.Post;
import net.jforum.entities.Topic;

import org.apache.commons.lang.ArrayUtils;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.hibernate.SessionFactory;
import org.hibernate.cache.Cache;
import org.hibernate.cache.QueryCache;
import org.hibernate.engine.SessionFactoryImplementor;

/**
 * @author Rafael Steil
 */
@Aspect
public class CacheEvictionRules {
    private SessionFactoryImplementor factoryImplementor;
    private SessionFactory sessionFactory;

    public CacheEvictionRules(SessionFactory factory) {
        if (factory instanceof SessionFactoryImplementor) {
            this.sessionFactory = factory;
            this.factoryImplementor = (SessionFactoryImplementor) factory;
        } else {
            this.sessionFactory = factory;
            //this.factoryImplementor = (SessionFactoryImplementor)((SpringSessionFactory)factory).getOriginal();
        }
    }

    /*
     * ******************
     * GROUPS SERVICE
     * ******************
     */
    @AfterReturning("execution (* net.jforum.services.GroupService.savePermissions(..))")
    public void permissionsChanged() {
        this.clearCacheRegion(this.factoryImplementor.getQueryCache("forumDAO.getModerators"));
    }

    /*
     * ****************************
     *       RANKING REPOSITORY
     * ****************************
     */
    @AfterReturning("(execution (* net.jforum.repository.Repository.add(..))"
            + " || execution (* net.jforum.repository.Repository.update(..))"
            + " || execution (* net.jforum.repository.Repository.remove(..)))"
            + " && target(net.jforum.repository.RankingRepository)")
    public void rankingChanged() {
        this.clearCacheRegion(this.factoryImplementor.getQueryCache("rankingDAO"));
    }

    /*
     * **************************
     *       SMILIE REPOSITORY
     * **************************
     *
     */
    @AfterReturning("(execution (* net.jforum.repository.Repository.add(..))"
            + " || execution (* net.jforum.repository.Repository.update(..))"
            + " || execution (* net.jforum.repository.Repository.remove(..)))"
            + " && target(net.jforum.repository.SmilieRepository)")
    public void smilieChanged() {
        this.clearCacheRegion(this.factoryImplementor.getQueryCache("smilieDAO"));
    }

    /*
     * *************************
     *       USER REPOSITORY
     * *************************
     */
    @AfterReturning("execution (* net.jforum.repository.Repository.add(..)) && target(net.jforum.repository.UserRepository)")
    public void newUserRegistered() {
        this.clearCacheRegion(this.factoryImplementor.getQueryCache("userDAO.getTotalUsers"));
        this.clearCacheRegion(this.factoryImplementor.getQueryCache("userDAO.getLastRegisteredUser"));
    }

    /*
     * *********************
     *       CONFIG
     * *********************
     * Changes to configurations stored in the database are very rare
     */
    @AfterReturning("(execution (* net.jforum.repository.Repository.add(..)) "
            + " || execution (* net.jforum.repository.Repository.update(..)))"
            + " && target(net.jforum.repository.ConfigRepository)")
    public void configChanged() {
        this.clearCacheRegion(this.factoryImplementor.getQueryCache("configDAO"));
    }

    /*
     * **************************
     *       TOPIC REPOSITORY
     * **************************
     */
    @AfterReturning("(execution (* net.jforum.repository.Repository.update(..)) && args(topic)) "
            + " && target(net.jforum.repository.TopicRepository)")
    public void topicUpdated(Topic topic) {
        this.clearCacheRegion(
                this.factoryImplementor.getQueryCache("forumDAO.getTopics#" + topic.getForum().getId()));
    }

    /*
     * *********************
     *       FORUM
     * *********************
     */
    @AfterReturning("execution (* net.jforum.services.ModerationService.moveTopics(..)) && args(toForumId, log, topicIds)")
    public void moveTopics(int toForumId, ModerationLog log, int... topicIds) {
        if (!ArrayUtils.isEmpty(topicIds)) {
            this.clearCacheRegion(this.factoryImplementor.getQueryCache("forumDAO.getTotalPosts#" + toForumId));
            this.clearCacheRegion(this.factoryImplementor.getQueryCache("forumDAO.getTotalTopics#" + toForumId));
            Cache cache = this.factoryImplementor.getSecondLevelCacheRegion("net.jforum.entities.Forum");

            if (cache != null) {
                cache.remove("net.jforum.entities.Forum#" + toForumId);
            }

            Topic topic = (Topic) this.sessionFactory.getCurrentSession().get(Topic.class, topicIds[0]);
            Forum forum = topic.getForum();

            this.clearCacheRegion(this.factoryImplementor.getQueryCache("forumDAO.getTotalPosts#" + forum.getId()));
            this.clearCacheRegion(
                    this.factoryImplementor.getQueryCache("forumDAO.getTotalTopics#" + forum.getId()));

            Cache cache2 = this.factoryImplementor.getSecondLevelCacheRegion("net.jforum.entities.Forum");

            if (cache2 != null) {
                cache2.remove("net.jforum.entities.Forum#" + forum.getId());
            }
        }
    }

    /**
     * Any change made by the Administrator. It's rare, to evict everything (easier)
     * Admin operation on Forums are rare, so just evict the entire region, as it
     * only happens rarely. Regular board usage may need to update a Forum
     * when a new Post is created, so we only need to evict that specific instance
     */
    @AfterReturning("execution (* net.jforum.services.ForumService.*(..)) ")
    public void forumChangedByAdministration() {
        this.clearCacheRegion(this.factoryImplementor.getSecondLevelCacheRegion("net.jforum.entities.Forum"));
        this.clearCacheRegion(this.factoryImplementor.getQueryCache("categoryDAO.getForums"));
    }

    @AfterReturning("execution (* net.jforum.services.PostService.delete(..)) && args(post)")
    public void postDeleted(Post post) {
        // We force the eviction of both totalPosts and totalTopics as removing a post
        // may trigger the deletion of a topic as well
        this.postOrTopicAddedOrDeletedRules(post.getForum().getId());
    }

    @AfterReturning("execution (* net.jforum.services.ModerationService.deleteTopics(..)) && args(topics, log)")
    public void topicDeleted(List<Topic> topics, ModerationLog log) {
        if (topics.size() > 0) {
            // We're considering that all topics belong to the same forum
            Forum forum = topics.get(0).getForum();
            this.postOrTopicAddedOrDeletedRules(forum.getId());
            this.clearCacheRegion(this.factoryImplementor.getQueryCache("rssDAO.getForumTopics#" + forum.getId()));
        }
    }

    /**
     * New post in the forum, so we must reload to it get the latest post instance
     */
    @AfterReturning("execution (* net.jforum.services.TopicService.addTopic(..)) && args(topic, pollOptions, attachments)")
    public void forumNewTopic(Topic topic, List<PollOption> pollOptions, List<Attachment> attachments) {
        if (!topic.isWaitingModeration()) {
            this.newForumPostRule(topic);
        }
    }

    /**
     * A new reply to an existing topic
     */
    @AfterReturning("execution (* net.jforum.services.TopicService.reply(..)) && args(topic, post, attachments)")
    public void forumNewPost(Topic topic, Post post, List<Attachment> attachments) {
        if (!post.isWaitingModeration()) {
            this.newForumPostRule(topic);
        }
    }

    @AfterReturning("execution (* net.jforum.services.ModerationService.approvePost(..)) && args(post)")
    public void postApproved(Post post) {
        this.newForumPostRule(post.getTopic());
    }

    /*
     * *********************
     *       CATEGORY
     * *********************
     * The rules for categories are simple, as they only change in the Admin,
     * so it's not a problem to evict the entire region
     */

    @AfterReturning("execution(* net.jforum.services.CategoryService.*(..))")
    public void categoryChanged() {
        this.clearCacheRegion(this.factoryImplementor.getSecondLevelCacheRegion("net.jforum.entities.Category"));
        this.clearCacheRegion(this.factoryImplementor.getQueryCache("categoryDAO.getAllCategories"));
    }

    private void newForumPostRule(Topic topic) {
        int forumId = topic.getForum().getId();
        this.postOrTopicAddedOrDeletedRules(forumId);
    }

    private void postOrTopicAddedOrDeletedRules(int forumId) {
        this.clearCacheRegion(this.factoryImplementor.getQueryCache("recentTopicsDAO"));
        this.clearCacheRegion(this.factoryImplementor.getQueryCache("forumDAO.getTotalPosts#" + forumId));
        this.clearCacheRegion(this.factoryImplementor.getQueryCache("forumDAO.getTotalTopics#" + forumId));
        this.clearCacheRegion(this.factoryImplementor.getQueryCache("forumDAO.getTotalMessages"));
        this.clearCacheRegion(this.factoryImplementor.getQueryCache("forumDAO.getTopics#" + forumId));
    }

    private void clearCacheRegion(Cache cache) {
        if (cache != null) {
            cache.clear();
        }
    }

    private void clearCacheRegion(QueryCache cache) {
        if (cache != null) {
            cache.clear();
        }
    }
}