/* * 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(); } } }