From 9cdf1e67b12f41c232984ed696d000f53da1e7bb Mon Sep 17 00:00:00 2001 From: thanhvc Date: Mon, 24 Sep 2012 14:57:12 +0700 Subject: [PATCH 1/6] KS-4619 | Performance problems when viewing Topic. --- .../ks/bench/wiki/MockForumService.java | 8 ++ .../forum/service/DataStorage.java | 21 ++++ .../forum/service/ForumService.java | 11 ++ .../service/cache/CachedDataStorage.java | 102 +++++++++++++++++- .../forum/service/cache/model/CacheType.java | 2 + .../cache/model/data/ListPostData.java | 17 +++ .../service/cache/model/data/PostData.java | 6 ++ .../service/cache/model/key/PostKey.java | 10 +- .../service/cache/model/key/PostListKey.java | 76 +++++++++++++ .../forum/service/impl/ForumServiceImpl.java | 10 ++ .../forum/service/impl/JCRDataStorage.java | 56 ++++++++++ .../impl/model/AbstractListAccess.java | 95 ++++++++++++++++ .../forum/service/impl/model/PostFilter.java | 82 ++++++++++++++ .../service/impl/model/PostListAccess.java | 86 +++++++++++++++ .../forum/test/TestForumService.java | 38 ++++++- .../webui/UIForumKeepStickPageIterator.java | 2 +- .../forum/webui/UITopicDetail.java | 82 ++++++++------ .../forum/service/FakeForumService.java | 8 ++ 18 files changed, 674 insertions(+), 38 deletions(-) create mode 100644 eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/data/ListPostData.java create mode 100644 eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/key/PostListKey.java create mode 100644 eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/AbstractListAccess.java create mode 100644 eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/PostFilter.java create mode 100644 eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/PostListAccess.java diff --git a/component/injector/src/test/java/org/exoplatform/ks/bench/wiki/MockForumService.java b/component/injector/src/test/java/org/exoplatform/ks/bench/wiki/MockForumService.java index 6ffb706c4..cf81b7fc7 100644 --- a/component/injector/src/test/java/org/exoplatform/ks/bench/wiki/MockForumService.java +++ b/component/injector/src/test/java/org/exoplatform/ks/bench/wiki/MockForumService.java @@ -24,6 +24,7 @@ import javax.jcr.NodeIterator; +import org.exoplatform.commons.utils.ListAccess; import org.exoplatform.container.component.ComponentPlugin; import org.exoplatform.forum.service.Category; import org.exoplatform.forum.service.Forum; @@ -48,6 +49,7 @@ import org.exoplatform.forum.service.TopicType; import org.exoplatform.forum.service.UserProfile; import org.exoplatform.forum.service.Watch; +import org.exoplatform.forum.service.impl.model.PostFilter; import org.exoplatform.services.organization.User; /** @@ -1285,4 +1287,10 @@ public void removeCacheUserProfile(String userName) throws Exception { } + @Override + public ListAccess getPosts(PostFilter filter) throws Exception { + // TODO Auto-generated method stub + return null; + } + } diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/DataStorage.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/DataStorage.java index e606dc629..b5067350e 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/DataStorage.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/DataStorage.java @@ -26,6 +26,7 @@ import javax.jcr.NodeIterator; import org.exoplatform.container.component.ComponentPlugin; +import org.exoplatform.forum.service.impl.model.PostFilter; import org.exoplatform.ks.common.conf.RoleRulesPlugin; import org.exoplatform.ks.common.jcr.KSDataLocation; import org.exoplatform.management.annotations.Managed; @@ -153,6 +154,26 @@ public interface DataStorage { long getLastReadIndex(String path, String isApproved, String isHidden, String userLogin) throws Exception; JCRPageList getPosts(String categoryId, String forumId, String topicId, String isApproved, String isHidden, String strQuery, String userLogin) throws Exception; + + /** + * Gets a post list by given PostFilter with offset and limit + * @param filter: specified PostFilter + * @param offset + * @param limit + * @return List of Posts + * @throws Exception + * @since 2.2.11 + */ + List getPosts(PostFilter filter, int offset, int limit) throws Exception; + + /** + * Gets count of post by given PostFilter + * @param filter: specified PostFilter + * @return + * @throws Exception + * @since 2.2.11 + */ + int getPostsCount(PostFilter filter) throws Exception; long getAvailablePost(String categoryId, String forumId, String topicId, String isApproved, String isHidden, String userLogin) throws Exception; diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/ForumService.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/ForumService.java index 18e76d72d..abeef3647 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/ForumService.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/ForumService.java @@ -24,7 +24,9 @@ import javax.jcr.NodeIterator; +import org.exoplatform.commons.utils.ListAccess; import org.exoplatform.container.component.ComponentPlugin; +import org.exoplatform.forum.service.impl.model.PostFilter; import org.exoplatform.services.organization.User; /** @@ -377,6 +379,15 @@ public interface ForumService extends ForumServiceLegacy { * @throws Exception the exception */ JCRPageList getPosts(String categoryId, String forumId, String topicId, String isApproved, String isHidden, String strQuery, String userLogin) throws Exception; + + /** + * Gets Posts and return ListAccess + * @param filter + * @return + * @throws Exception + * @since 2.2.11 + */ + ListAccess getPosts(PostFilter filter) throws Exception; /** * Gets posts of topic. diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java index 5cb59013c..dd0e90327 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java @@ -45,6 +45,7 @@ import org.exoplatform.forum.service.cache.model.data.ListCategoryData; import org.exoplatform.forum.service.cache.model.data.ListForumData; import org.exoplatform.forum.service.cache.model.data.ListLinkData; +import org.exoplatform.forum.service.cache.model.data.ListPostData; import org.exoplatform.forum.service.cache.model.data.ListWatchData; import org.exoplatform.forum.service.cache.model.data.PostData; import org.exoplatform.forum.service.cache.model.data.SimpleCacheData; @@ -57,12 +58,15 @@ import org.exoplatform.forum.service.cache.model.key.ForumListKey; import org.exoplatform.forum.service.cache.model.key.LinkListKey; import org.exoplatform.forum.service.cache.model.key.ObjectNameKey; +import org.exoplatform.forum.service.cache.model.key.PostKey; +import org.exoplatform.forum.service.cache.model.key.PostListKey; import org.exoplatform.forum.service.cache.model.key.SimpleCacheKey; import org.exoplatform.forum.service.cache.model.key.TopicKey; import org.exoplatform.forum.service.cache.model.selector.CategoryIdSelector; import org.exoplatform.forum.service.cache.model.selector.ForumPathSelector; import org.exoplatform.forum.service.cache.model.selector.ScopeCacheSelector; import org.exoplatform.forum.service.impl.JCRDataStorage; +import org.exoplatform.forum.service.impl.model.PostFilter; import org.exoplatform.ks.common.conf.RoleRulesPlugin; import org.exoplatform.ks.common.jcr.KSDataLocation; import org.exoplatform.management.annotations.Managed; @@ -91,6 +95,8 @@ public class CachedDataStorage implements DataStorage, Startable { private ExoCache categoryList; private ExoCache forumData; private ExoCache forumList; + private ExoCache postData; + private ExoCache postList; private ExoCache topicData; private ExoCache watchListData; private ExoCache linkListData; @@ -102,6 +108,8 @@ public class CachedDataStorage implements DataStorage, Startable { private FutureExoCache> categoryListFuture; private FutureExoCache> forumDataFuture; private FutureExoCache> forumListFuture; + private FutureExoCache> postDataFuture; + private FutureExoCache> postListFuture; private FutureExoCache> topicDataFuture; private FutureExoCache> watchListDataFuture; private FutureExoCache> linkListDataFuture; @@ -152,6 +160,10 @@ private void clearTopicCache(String categoryId, String forumId, String topicId) } } + private void clearPostCache(String categoryId, String forumId, String topicId, String postId) throws Exception { + postData.remove(new PostKey(categoryId, forumId, topicId, postId)); + } + private void clearObjectCache(Forum forum, boolean isPutNewKey) throws Exception { if (forum != null) { ForumData forumData = new ForumData(forum); @@ -204,6 +216,8 @@ public void start() { this.categoryList = CacheType.CATEGORY_LIST.getFromService(service); this.forumData = CacheType.FORUM_DATA.getFromService(service); this.forumList = CacheType.FORUM_LIST.getFromService(service); + this.postData = CacheType.POST_DATA.getFromService(service); + this.postList = CacheType.POST_LIST.getFromService(service); this.topicData = CacheType.TOPIC_DATA.getFromService(service); this.objectNameData = CacheType.OBJECT_NAME_DATA.getFromService(service); this.miscData = CacheType.MISC_DATA.getFromService(service); @@ -215,6 +229,8 @@ public void start() { this.categoryListFuture = CacheType.CATEGORY_LIST.createFutureCache(categoryList); this.forumDataFuture = CacheType.FORUM_DATA.createFutureCache(forumData); this.forumListFuture = CacheType.FORUM_LIST.createFutureCache(forumList); + this.postDataFuture = CacheType.POST_DATA.createFutureCache(postData); + this.postListFuture = CacheType.POST_LIST.createFutureCache(postList); this.topicDataFuture = CacheType.TOPIC_DATA.createFutureCache(topicData); this.watchListDataFuture = CacheType.WATCH_LIST_DATA.createFutureCache(watchListData); this.linkListDataFuture = CacheType.LINK_LIST_DATA.createFutureCache(linkListData); @@ -269,6 +285,32 @@ private List buildWatchOutput(ListWatchData data) { } + private ListPostData buildPostInput(List posts) { + List data = new ArrayList(); + for (Post p : posts) { + data.add(new PostKey(p)); + } + return new ListPostData(data); + } + + private List buildPostOutput(ListPostData data) { + + if (data == null) { + return null; + } + + List out = new ArrayList(); + for (PostKey k : data.getIds()) { + try { + out.add(getPost(k.getCategory(), k.getForum(), k.getTopic(), k.getPost())); + } catch (Exception e) { + LOG.error(e); + } + } + return out; + + } + private ListForumData buildForumInput(List forums) { List keys = new ArrayList(); for (Forum f : forums) { @@ -668,19 +710,46 @@ public JCRPageList getPagePostByUser(String userName, String userId, boolean isM return storage.getPagePostByUser(userName, userId, isMod, strOrderBy); } - public Post getPost(String categoryId, String forumId, String topicId, String postId) throws Exception { - return storage.getPost(categoryId, forumId, topicId, postId); + public Post getPost(final String categoryId, final String forumId, final String topicId, final String postId) throws Exception { + + PostKey key = new PostKey(categoryId, forumId, topicId, postId); + + return postDataFuture.get( + new ServiceContext() { + + public PostData execute() { + try { + Post got = storage.getPost(categoryId, forumId, topicId, postId); + if (got == null) { + return PostData.NULL; + } else { + return new PostData(got); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }, + key + ).build(); + } public JCRPageList getListPostsByIP(String ip, String strOrderBy) throws Exception { return storage.getListPostsByIP(ip, strOrderBy); } + + public void savePost(String categoryId, String forumId, String topicId, Post post, boolean isNew, MessageBuilder messageBuilder) throws Exception { storage.savePost(categoryId, forumId, topicId, post, isNew, messageBuilder); clearForumCache(categoryId, forumId, false); clearForumListCache(); clearTopicCache(categoryId, forumId, topicId); + // + if (isNew == false) { + clearPostCache(categoryId, forumId, topicId, post.getId()); + } statistic = null; } @@ -693,6 +762,7 @@ public Post removePost(String categoryId, String forumId, String topicId, String clearForumCache(categoryId, forumId, false); clearForumListCache(); clearTopicCache(categoryId, forumId, topicId); + clearPostCache(categoryId, forumId, topicId, postId); statistic = null; } catch (Exception e) { LOG.error(e.getMessage(), e); @@ -1241,4 +1311,30 @@ public InputStream createForumRss(String objectId, String link) throws Exception public InputStream createUserRss(String userId, String link) throws Exception { return storage.createUserRss(userId, link); } -} + + public List getPosts(final PostFilter filter, final int offset, final int limit) throws Exception { + + PostListKey key = new PostListKey(filter, offset, limit); + + return buildPostOutput(postListFuture.get( + new ServiceContext() { + public ListPostData execute() { + try { + List got = storage.getPosts(filter, offset, limit); + return buildPostInput(got); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }, + key + )); + + } + + public int getPostsCount(PostFilter filter) throws Exception { + return storage.getPostsCount(filter); + } + + +} \ No newline at end of file diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/CacheType.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/CacheType.java index e30db4a68..aa715eead 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/CacheType.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/CacheType.java @@ -18,6 +18,8 @@ public enum CacheType { FORUM_LIST("forum.ForumList"), TOPIC_DATA("forum.TopicData"), TOPIC_LIST("forum.TopicList"), + POST_DATA("forum.PostData"), + POST_LIST("forum.PostList"), WATCH_LIST_DATA("forum.WatchListData"), LINK_LIST_DATA("forum.LinkListData"), OBJECT_NAME_DATA("forum.ObjectNameData"), diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/data/ListPostData.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/data/ListPostData.java new file mode 100644 index 000000000..1f8f420f6 --- /dev/null +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/data/ListPostData.java @@ -0,0 +1,17 @@ +package org.exoplatform.forum.service.cache.model.data; + +import org.exoplatform.forum.service.cache.model.AbstractListData; +import org.exoplatform.forum.service.cache.model.key.PostKey; + +import java.util.List; + +/** + * @author Alain Defrance + */ +public class ListPostData extends AbstractListData { + + public ListPostData(List ids) { + super(ids); + } + +} \ No newline at end of file diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/data/PostData.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/data/PostData.java index 7e26f9eb8..d334d7edc 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/data/PostData.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/data/PostData.java @@ -13,6 +13,8 @@ */ public class PostData implements CachedData { + public final static PostData NULL = new PostData(new Post()); + private final String id; private final String path; private final String owner; @@ -61,6 +63,10 @@ public PostData(Post post) { public Post build() { + if (this == NULL) { + return null; + } + Post post = new Post(); post.setId(this.id); diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/key/PostKey.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/key/PostKey.java index aded527ea..fe024a218 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/key/PostKey.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/key/PostKey.java @@ -21,7 +21,13 @@ public PostKey(String category, String forum, String topic, String post) { } public PostKey(Post post) { - this.category = null; // TODO : improve + + int endPos = post.getPath().indexOf(post.getForumId()) - 1; + String catId = post.getPath().substring(0, endPos); + int startPos = catId.lastIndexOf("/") + 1; + catId = catId.substring(startPos); + + this.category = catId; this.forum = post.getForumId(); this.topic = post.getTopicId(); this.post = post.getId(); @@ -68,4 +74,4 @@ public int hashCode() { result = 31 * result + (post != null ? post.hashCode() : 0); return result; } -} +} \ No newline at end of file diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/key/PostListKey.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/key/PostListKey.java new file mode 100644 index 000000000..2cd04af4e --- /dev/null +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/key/PostListKey.java @@ -0,0 +1,76 @@ +package org.exoplatform.forum.service.cache.model.key; + +import org.exoplatform.forum.service.cache.model.ScopeCacheKey; +import org.exoplatform.forum.service.impl.model.PostFilter; + +/** + * @author Alain Defrance + */ +public class PostListKey extends ScopeCacheKey { + + private String categoryId; + private String forumId; + private String topicId; + private String isApproved; + private String isHidden; + private String isWaiting; + private String topicPath; + private String userLogin; + + private int offset; + private int limit; + + public PostListKey(PostFilter filter, int offset, int limit) { + + this.categoryId = filter.getCategoryId(); + this.forumId = filter.getForumId(); + this.topicId = filter.getTopicId(); + this.isApproved = filter.getIsApproved(); + this.isHidden = filter.getIsHidden(); + this.isWaiting = filter.getIsWaiting(); + this.topicPath = filter.getTopicPath(); + this.userLogin = filter.getUserLogin(); + this.offset = offset; + this.limit = limit; + + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PostListKey)) return false; + if (!super.equals(o)) return false; + + PostListKey that = (PostListKey) o; + + if (limit != that.limit) return false; + if (offset != that.offset) return false; + if (categoryId != null ? !categoryId.equals(that.categoryId) : that.categoryId != null) return false; + if (forumId != null ? !forumId.equals(that.forumId) : that.forumId != null) return false; + if (isApproved != null ? !isApproved.equals(that.isApproved) : that.isApproved != null) return false; + if (isHidden != null ? !isHidden.equals(that.isHidden) : that.isHidden != null) return false; + if (isWaiting != null ? !isWaiting.equals(that.isWaiting) : that.isWaiting != null) return false; + if (topicId != null ? !topicId.equals(that.topicId) : that.topicId != null) return false; + if (topicPath != null ? !topicPath.equals(that.topicPath) : that.topicPath != null) return false; + if (userLogin != null ? !userLogin.equals(that.userLogin) : that.userLogin != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (categoryId != null ? categoryId.hashCode() : 0); + result = 31 * result + (forumId != null ? forumId.hashCode() : 0); + result = 31 * result + (topicId != null ? topicId.hashCode() : 0); + result = 31 * result + (isApproved != null ? isApproved.hashCode() : 0); + result = 31 * result + (isHidden != null ? isHidden.hashCode() : 0); + result = 31 * result + (isWaiting != null ? isWaiting.hashCode() : 0); + result = 31 * result + (topicPath != null ? topicPath.hashCode() : 0); + result = 31 * result + (userLogin != null ? userLogin.hashCode() : 0); + result = 31 * result + offset; + result = 31 * result + limit; + return result; + } + +} \ No newline at end of file diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/ForumServiceImpl.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/ForumServiceImpl.java index c0f6e407d..a2c15b072 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/ForumServiceImpl.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/ForumServiceImpl.java @@ -30,6 +30,7 @@ import javax.jcr.NodeIterator; +import org.exoplatform.commons.utils.ListAccess; import org.exoplatform.container.ExoContainerContext; import org.exoplatform.container.component.ComponentPlugin; import org.exoplatform.container.xml.InitParams; @@ -63,6 +64,8 @@ import org.exoplatform.forum.service.UserProfile; import org.exoplatform.forum.service.Utils; import org.exoplatform.forum.service.Watch; +import org.exoplatform.forum.service.impl.model.PostFilter; +import org.exoplatform.forum.service.impl.model.PostListAccess; import org.exoplatform.ks.common.CommonUtils; import org.exoplatform.ks.common.conf.RoleRulesPlugin; import org.exoplatform.management.annotations.ManagedBy; @@ -514,6 +517,13 @@ public JCRPageList getPostForSplitTopic(String topicPath) throws Exception { public JCRPageList getPosts(String categoryId, String forumId, String topicId, String isApproved, String isHidden, String strQuery, String userLogin) throws Exception { return storage.getPosts(categoryId, forumId, topicId, isApproved, isHidden, strQuery, userLogin); } + + /** + * {@inheritDoc} + */ + public ListAccess getPosts(PostFilter filter) throws Exception { + return new PostListAccess(PostListAccess.Type.POSTS, storage, filter); + } /** * {@inheritDoc} diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/JCRDataStorage.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/JCRDataStorage.java index db05ccef9..f796c330d 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/JCRDataStorage.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/JCRDataStorage.java @@ -104,6 +104,7 @@ import org.exoplatform.forum.service.conf.PostData; import org.exoplatform.forum.service.conf.StatisticEventListener; import org.exoplatform.forum.service.conf.TopicData; +import org.exoplatform.forum.service.impl.model.PostFilter; import org.exoplatform.forum.service.user.AutoPruneJob; import org.exoplatform.ks.common.CommonUtils; import org.exoplatform.ks.common.TransformHTML; @@ -2827,6 +2828,61 @@ public JCRPageList getPosts(String categoryId, String forumId, String topicId, S return null; } } + + private String makePostsQuery(PostFilter filter) throws Exception { + String topicPath = filter.getTopicPath(); + if(Utils.isEmpty(topicPath)) { + topicPath = new StringBuffer("/"+dataLocator.getForumCategoriesLocation()) + .append("/").append(filter.getCategoryId()).append("/") + .append(filter.getForumId()).append("/").append(filter.getTopicId()).toString(); + } + + StringBuilder strBuilder = new StringBuilder(JCR_ROOT) + .append(topicPath).append("//element(*,").append(EXO_POST).append(")") + .append(Utils.getPathQuery(filter.getIsApproved(), filter.getIsHidden(), filter.getIsWaiting(), filter.getUserLogin())) + .append(" order by @exo:createdDate ascending"); + + return strBuilder.toString(); + } + + @Override + public List getPosts(PostFilter filter, int offset, int limit) throws Exception { + SessionProvider sProvider = CommonUtils.createSystemProvider(); + try { + QueryManager qm = getForumHomeNode(sProvider).getSession().getWorkspace().getQueryManager(); + QueryImpl query = (QueryImpl) qm.createQuery(makePostsQuery(filter), Query.XPATH); + query.setOffset(offset); + query.setLimit(limit); + QueryResult result = query.execute(); + NodeIterator iter = result.getNodes(); + Node currentNode = null; + List posts = new ArrayList((int)iter.getSize()); + while (iter.hasNext()) { + currentNode = iter.nextNode(); + posts.add(getPost(currentNode)); + } + return posts; + } catch(Exception e) { + logDebug("Failed to get posts by filter of topic " + filter.getTopicId(), e); + return new ArrayList(); + } + + } + + public int getPostsCount(PostFilter filter) throws Exception { + SessionProvider sProvider = CommonUtils.createSystemProvider(); + try { + // + QueryManager qm = getForumHomeNode(sProvider).getSession().getWorkspace().getQueryManager(); + Query query = qm.createQuery(makePostsQuery(filter).toString(), Query.XPATH); + QueryResult result = query.execute(); + + return (int)result.getNodes().getSize(); + } catch(Exception e) { + return 0; + } + + } public long getAvailablePost(String categoryId, String forumId, String topicId, String isApproved, String isHidden, String userLogin) throws Exception { SessionProvider sProvider = CommonUtils.createSystemProvider(); diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/AbstractListAccess.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/AbstractListAccess.java new file mode 100644 index 000000000..2f5b9d11e --- /dev/null +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/AbstractListAccess.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2003-2012 eXo Platform SAS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.exoplatform.forum.service.impl.model; + +import org.exoplatform.commons.utils.ListAccess; +import org.exoplatform.forum.service.DataStorage; + +/** + * Created by The eXo Platform SAS Author : thanh_vucong + * thanh_vucong@exoplatform.com Sep 17, 2012 + * @since 2.2.11 + */ +public abstract class AbstractListAccess implements ListAccess { + + protected DataStorage storage = null; + + private int currentPage = 1; + + private int totalPage = 1; + + private int pageSize = 0; + + protected int size = -1; + + public abstract E[] load(int pageSelect) throws Exception, IllegalArgumentException; + + public void reCalculate(int offset, int limit) { + if (offset >= 0) { + currentPage = (offset + 1) / limit; + if ((offset + 1) % limit > 0) + currentPage++; + } + } + + public void initialize(int pageSize, int pageSelect) throws Exception { + this.setPageSize(pageSize); + this.getTotalPages(); + this.setCurrentPage(pageSelect); + } + + public int getTotalPages() throws Exception { + this.totalPage = getSize() / pageSize; + if (getSize() % pageSize > 0) + this.totalPage++; + return this.totalPage; + } + + public int getPageSize() { + return this.pageSize; + } + + public void setPageSize(int pageSize) { + this.pageSize = pageSize; + } + + public int getCurrentPage() throws Exception { + return currentPage; + } + + public void setCurrentPage(int page) throws Exception { + if (page > totalPage && totalPage > 0) { + currentPage = totalPage; + } else if (page <= 0) { + currentPage = 1; + } else { + currentPage = page; + } + } + + public int getOffset(int currentPage) { + return (currentPage - 1) * pageSize; + } + + public int getOffset(int currentPage, int pageSize) { + return (this.currentPage - 1) * this.pageSize; + } + + public int getOffset() { + return (this.currentPage - 1) * this.pageSize; + } +} \ No newline at end of file diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/PostFilter.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/PostFilter.java new file mode 100644 index 000000000..0221186e6 --- /dev/null +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/PostFilter.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2003-2012 eXo Platform SAS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.exoplatform.forum.service.impl.model; + +/** + * Created by The eXo Platform SAS + * Author : thanh_vucong + * thanh_vucong@exoplatform.com + * Sep 13, 2012 + * @since 2.2.11 + */ +public class PostFilter { + + private String categoryId = null; + private String forumId = null; + private String topicId = null; + private String isApproved = null; + private String isWaiting = null; + private String isHidden = null; + private String userLogin = null; + + private String topicPath = null; + + public PostFilter(String categoryId, String forumId, String topicId, String isApproved, String isHidden, String isWaiting, String userLogin) { + this.categoryId = categoryId; + this.forumId = forumId; + this.topicId = topicId; + this.isApproved = isApproved; + this.isWaiting = isWaiting; + this.isHidden = isHidden; + this.userLogin = userLogin; + } + + public PostFilter(String topicPath) { + this.topicPath = topicPath; + } + + + public String getTopicPath() { + return topicPath; + } + + + public String getCategoryId() { + return categoryId; + } + public String getForumId() { + return forumId; + } + public String getTopicId() { + return topicId; + } + public String getIsApproved() { + return isApproved; + } + public String getIsWaiting() { + return isWaiting; + } + public String getIsHidden() { + return isHidden; + } + public String getUserLogin() { + return userLogin; + } + + + +} \ No newline at end of file diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/PostListAccess.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/PostListAccess.java new file mode 100644 index 000000000..a7b5fda22 --- /dev/null +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/PostListAccess.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2003-2012 eXo Platform SAS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.exoplatform.forum.service.impl.model; + +import java.util.List; + +import org.exoplatform.forum.service.DataStorage; +import org.exoplatform.forum.service.Post; + +/** + * Created by The eXo Platform SAS + * Author : thanh_vucong + * thanh_vucong@exoplatform.com + * Sep 13, 2012 + * @since 2.2.11 + */ +public class PostListAccess extends AbstractListAccess { + + private PostFilter filter = null; + + private Type type; + + public enum Type { + POSTS + } + + public PostListAccess(Type type, DataStorage storage, PostFilter filter) { + this.storage = storage; + this.filter = filter; + this.type = type; + } + + @Override + public Post[] load(int offset, int limit) throws Exception, IllegalArgumentException { + List got = null; + + switch(type) { + case POSTS : + got = storage.getPosts(filter, offset, limit); + break; + } + // + reCalculate(offset, limit); + + return got.toArray(new Post[got.size()]); + + } + + @Override + public int getSize() throws Exception { + if (size < 0) { + switch(type) { + case POSTS : + size = storage.getPostsCount(filter); + break; + } + } + return size; + } + + public PostFilter getFilter() { + return filter; + } + + @Override + public Post[] load(int pageSelect) throws Exception, IllegalArgumentException { + int offset = getOffset(pageSelect); + int limit = getPageSize(); + return load(offset, limit); + } + +} \ No newline at end of file diff --git a/eXoApplication/forum/service/src/test/java/org/exoplatform/forum/test/TestForumService.java b/eXoApplication/forum/service/src/test/java/org/exoplatform/forum/test/TestForumService.java index c036d9b5d..f4ac389ea 100755 --- a/eXoApplication/forum/service/src/test/java/org/exoplatform/forum/test/TestForumService.java +++ b/eXoApplication/forum/service/src/test/java/org/exoplatform/forum/test/TestForumService.java @@ -39,9 +39,11 @@ import org.exoplatform.forum.service.UserProfile; import org.exoplatform.forum.service.Utils; import org.exoplatform.forum.service.Watch; +import org.exoplatform.forum.service.impl.model.PostFilter; +import org.exoplatform.forum.service.impl.model.PostListAccess; import org.exoplatform.ks.common.UserHelper; -import org.exoplatform.ks.common.jcr.KSDataLocation; -import org.exoplatform.ks.common.jcr.PropertyReader; +import org.exoplatform.ks.common.jcr.KSDataLocation; +import org.exoplatform.ks.common.jcr.PropertyReader; import org.exoplatform.services.jcr.ext.app.SessionProviderService; import org.exoplatform.services.jcr.util.IdGenerator; import org.exoplatform.services.security.ConversationState; @@ -564,6 +566,38 @@ public void testPost() throws Exception { // getViewPost } + + public void testPostListAccess() throws Exception { + // set Data + setData(); + + List posts = new ArrayList(); + for (int i = 0; i < 25; ++i) { + Post post = createdPost(); + posts.add(post); + forumService_.savePost(categoryId, forumId, topicId, post, true, new MessageBuilder()); + } + // getPost + assertNotNull(forumService_.getPost(categoryId, forumId, topicId, posts.get(0).getId())); + assertEquals(25, forumService_.getTopic(categoryId, forumId, topicId, "").getPostCount()); + + // get ListPost + PostListAccess listAccess = (PostListAccess) forumService_.getPosts(new PostFilter(categoryId, forumId, topicId, "", "", "", "root")); + listAccess.initialize(10, 1); + assertEquals(listAccess.getSize(), posts.size() + 1);// size = 26 (first post and new postList) + + //Page 1 + List got = Arrays.asList(listAccess.load(1)); + assertEquals(got.size(), 10); + + //Page 2 + got = Arrays.asList(listAccess.load(2)); + assertEquals(got.size(), 10); + + //Page 3 + got = Arrays.asList(listAccess.load(3)); + assertEquals(got.size(), 6); + } // BookMark public void testBookMark() throws Exception { diff --git a/eXoApplication/forum/webapp/src/main/java/org/exoplatform/forum/webui/UIForumKeepStickPageIterator.java b/eXoApplication/forum/webapp/src/main/java/org/exoplatform/forum/webui/UIForumKeepStickPageIterator.java index dca0c2cca..7323cdafd 100644 --- a/eXoApplication/forum/webapp/src/main/java/org/exoplatform/forum/webui/UIForumKeepStickPageIterator.java +++ b/eXoApplication/forum/webapp/src/main/java/org/exoplatform/forum/webui/UIForumKeepStickPageIterator.java @@ -95,7 +95,7 @@ public String getURLGopage(String number) throws Exception { } public List getTotalpage() throws Exception { - int max_Page = pageList.getAvailablePage(); + int max_Page = maxPage; if (this.pageSelect > max_Page) this.pageSelect = max_Page; int page = this.pageSelect; diff --git a/eXoApplication/forum/webapp/src/main/java/org/exoplatform/forum/webui/UITopicDetail.java b/eXoApplication/forum/webapp/src/main/java/org/exoplatform/forum/webui/UITopicDetail.java index ec1192dc2..dac381436 100644 --- a/eXoApplication/forum/webapp/src/main/java/org/exoplatform/forum/webui/UITopicDetail.java +++ b/eXoApplication/forum/webapp/src/main/java/org/exoplatform/forum/webui/UITopicDetail.java @@ -49,6 +49,8 @@ import org.exoplatform.forum.service.Topic; import org.exoplatform.forum.service.UserProfile; import org.exoplatform.forum.service.Utils; +import org.exoplatform.forum.service.impl.model.PostFilter; +import org.exoplatform.forum.service.impl.model.PostListAccess; import org.exoplatform.forum.webui.popup.UIMovePostForm; import org.exoplatform.forum.webui.popup.UIMoveTopicForm; import org.exoplatform.forum.webui.popup.UIPageListPostHidden; @@ -207,7 +209,9 @@ public class UITopicDetail extends UIForumKeepStickPageIterator { public static final String SIGNATURE = "SignatureTypeID"; RenderHelper renderHelper = new RenderHelper(); - + + private PostListAccess postListAccess; + public UITopicDetail() throws Exception { isDoubleClickQuickReply = false; @@ -595,15 +599,10 @@ public void initPage() throws Exception { if (!isMod && !(this.topic.getOwner().equals(userName))) isApprove = "true"; } - pageList = getForumService().getPosts(this.categoryId, this.forumId, topicId, isApprove, isHidden, isWaiting, userName); - int maxPost = (int)this.userProfile.getMaxPostInPage(); - if (maxPost <= 0) - maxPost = 10; - pageList.setPageSize(maxPost); - maxPage = pageList.getAvailablePage(); - if (IdPostView.equals("lastpost") || this.pageSelect > maxPage) { - this.pageSelect = maxPage; - } + this.postListAccess = (PostListAccess) getForumService().getPosts(new PostFilter(this.categoryId, this.forumId, topicId, isApprove, isHidden, isWaiting, userName)); + + int pageSize = (int)this.userProfile.getMaxPostInPage(); + postListAccess.initialize(pageSize, pageSelect); } catch (Exception e) { log.warn("Failed to init topic page: " + e.getMessage(), e); } @@ -613,21 +612,37 @@ protected boolean getIsModeratePost() { return this.isModeratePost; } - @SuppressWarnings("unchecked") + @Override + public List getInfoPage() throws Exception { + List temp = new ArrayList(); + try { + temp.add(postListAccess.getPageSize()); + temp.add(postListAccess.getCurrentPage()); + temp.add(postListAccess.getSize()); + temp.add(postListAccess.getTotalPages()); + } catch (Exception e) { + temp.add(1); + temp.add(1); + temp.add(1); + temp.add(1); + } + return temp; + } + public List getPostPageList() throws Exception { - List posts = new ArrayList(); - if (this.pageList == null) - return posts; + Post[] posts = null; + + int pageSize = (int)this.userProfile.getMaxPostInPage(); try { try { if (!ForumUtils.isEmpty(lastPostId)) { - int maxPost = (int)this.userProfile.getMaxPostInPage(); + Long index = getForumService().getLastReadIndex((categoryId + ForumUtils.SLASH + forumId + ForumUtils.SLASH + topicId + ForumUtils.SLASH + lastPostId), isApprove, isHidden, userName); - if (index.intValue() <= maxPost) + if (index.intValue() <= pageSize) pageSelect = 1; else { - pageSelect = (int) (index / maxPost); - if (maxPost * pageSelect < index) + pageSelect = (int) (index / pageSize); + if (pageSize * pageSelect < index) pageSelect = pageSelect + 1; } lastPostId = ForumUtils.EMPTY_STR; @@ -635,20 +650,27 @@ public List getPostPageList() throws Exception { } catch (Exception e) { log.warn("Failed to find last read index for topic: " + e.getMessage(), e); } - posts = pageList.getPage(pageSelect); - pageSelect = pageList.getCurrentPage(); + postListAccess.setCurrentPage(pageSelect); + this.pageSelect = postListAccess.getCurrentPage(); + + maxPage = postListAccess.getTotalPages(); + + posts = postListAccess.load(pageSelect); + this.pageSelect = postListAccess.getCurrentPage(); + pagePostRemember.put(topicId, pageSelect); - if (posts == null) - posts = new ArrayList(); + List userNames = new ArrayList(); mapUserProfile.clear(); for (Post post : posts) { if (!userNames.contains(post.getOwner())) userNames.add(post.getOwner()); - if (getUICheckBoxInput(post.getId()) != null) { - getUICheckBoxInput(post.getId()).setChecked(false); - } else { - addUIFormInput(new UICheckBoxInput(post.getId(), post.getId(), false)); + synchronized(this) { + if (getUICheckBoxInput(post.getId()) != null) { + getUICheckBoxInput(post.getId()).setChecked(false); + } else { + addUIFormInput(new UICheckBoxInput(post.getId(), post.getId(), false)); + } } this.IdLastPost = post.getId(); } @@ -674,7 +696,7 @@ public List getPostPageList() throws Exception { } catch (Exception e) { log.warn("Failed to load posts page: " + e.getMessage(), e); } - return posts; + return Arrays.asList(posts); } public List getTagsByTopic() throws Exception { @@ -924,8 +946,8 @@ public void onEvent(Event event, UITopicDetail topicDetail, final } else { if (page == 0) { page = 1; - } else if (page > topicDetail.pageList.getAvailablePage()) { - page = topicDetail.pageList.getAvailablePage(); + } else if (page > topicDetail.postListAccess.getTotalPages()) { + page = topicDetail.postListAccess.getTotalPages(); } topicDetail.pageSelect = page; refresh(); @@ -1730,4 +1752,4 @@ public String renderPost(Post post) throws RenderingException { return renderHelper.renderPost(post); } -} +} diff --git a/eXoApplication/forum/webapp/src/test/java/org/exoplatform/forum/service/FakeForumService.java b/eXoApplication/forum/webapp/src/test/java/org/exoplatform/forum/service/FakeForumService.java index 9d06a0d19..d17d61449 100644 --- a/eXoApplication/forum/webapp/src/test/java/org/exoplatform/forum/service/FakeForumService.java +++ b/eXoApplication/forum/webapp/src/test/java/org/exoplatform/forum/service/FakeForumService.java @@ -28,7 +28,9 @@ import javax.jcr.NodeIterator; +import org.exoplatform.commons.utils.ListAccess; import org.exoplatform.container.component.ComponentPlugin; +import org.exoplatform.forum.service.impl.model.PostFilter; import org.exoplatform.ks.bbcode.api.BBCode; import org.exoplatform.services.organization.User; @@ -774,4 +776,10 @@ public void calculateDeletedGroup(String groupId, String groupName) throws Excep } + @Override + public ListAccess getPosts(PostFilter filter) throws Exception { + // TODO Auto-generated method stub + return null; + } + } From e0c18bdce4a2a768648aedfd5021225930318732 Mon Sep 17 00:00:00 2001 From: thanhvc Date: Mon, 24 Sep 2012 17:02:20 +0700 Subject: [PATCH 2/6] KS-4619 | Performance problems when viewing Topic - Clear PostList caching. --- .../forum/service/cache/CachedDataStorage.java | 17 +++++++++++++++++ .../exoplatform/forum/webui/UITopicDetail.java | 3 +++ 2 files changed, 20 insertions(+) diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java index dd0e90327..e66735eff 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java @@ -145,6 +145,10 @@ private void clearForumListCache() throws Exception { forumList.select(new ScopeCacheSelector()); } + private void clearPostListCache() throws Exception { + postList.select(new ScopeCacheSelector()); + } + private void clearLinkListCache() throws Exception { linkListData.select(new ScopeCacheSelector()); } @@ -746,6 +750,7 @@ public void savePost(String categoryId, String forumId, String topicId, Post pos clearForumCache(categoryId, forumId, false); clearForumListCache(); clearTopicCache(categoryId, forumId, topicId); + clearPostListCache(); // if (isNew == false) { clearPostCache(categoryId, forumId, topicId, post.getId()); @@ -755,6 +760,17 @@ public void savePost(String categoryId, String forumId, String topicId, Post pos public void modifyPost(List posts, int type) { storage.modifyPost(posts, type); + //clear caching + try { + clearPostListCache(); + // + for(Post post : posts) { + String categoryId = Utils.getCategoryId(post.getPath()); + clearPostCache(categoryId, post.getForumId(), post.getTopicId(), post.getId()); + } + } catch (Exception e) { + LOG.error(e.getMessage(), e); + } } public Post removePost(String categoryId, String forumId, String topicId, String postId) { @@ -762,6 +778,7 @@ public Post removePost(String categoryId, String forumId, String topicId, String clearForumCache(categoryId, forumId, false); clearForumListCache(); clearTopicCache(categoryId, forumId, topicId); + clearPostListCache(); clearPostCache(categoryId, forumId, topicId, postId); statistic = null; } catch (Exception e) { diff --git a/eXoApplication/forum/webapp/src/main/java/org/exoplatform/forum/webui/UITopicDetail.java b/eXoApplication/forum/webapp/src/main/java/org/exoplatform/forum/webui/UITopicDetail.java index dac381436..d19253675 100644 --- a/eXoApplication/forum/webapp/src/main/java/org/exoplatform/forum/webui/UITopicDetail.java +++ b/eXoApplication/forum/webapp/src/main/java/org/exoplatform/forum/webui/UITopicDetail.java @@ -603,6 +603,9 @@ public void initPage() throws Exception { int pageSize = (int)this.userProfile.getMaxPostInPage(); postListAccess.initialize(pageSize, pageSelect); + if (IdPostView.equals("lastpost")) { + this.pageSelect = postListAccess.getTotalPages(); + } } catch (Exception e) { log.warn("Failed to init topic page: " + e.getMessage(), e); } From 9ace481ffe7015f9ef629b64b41f020b44cfe4aa Mon Sep 17 00:00:00 2001 From: thanhvc Date: Wed, 26 Sep 2012 16:23:37 +0700 Subject: [PATCH 3/6] KS-4619 | Performance problems when viewing Topic -Adds caching:getPostsCount; add new DelayWritesJob --- .../ks/bench/wiki/MockForumService.java | 15 +++ .../forum/service/DataStorage.java | 12 ++ .../forum/service/ForumService.java | 12 ++ .../service/cache/CachedDataStorage.java | 26 +++- .../forum/service/conf/DelayWritesJob.java | 59 ++++++++ .../forum/service/impl/ForumServiceImpl.java | 14 ++ .../forum/service/impl/JCRDataStorage.java | 126 +++++++++++++----- .../forum/service/impl/model/PostFilter.java | 15 ++- .../forum/service/FakeForumService.java | 6 + .../ks/forum/cache-configuration.xml | 30 +++-- .../ks-extension/ks/ks-configuration.xml | 22 +++ 11 files changed, 289 insertions(+), 48 deletions(-) create mode 100644 eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/conf/DelayWritesJob.java diff --git a/component/injector/src/test/java/org/exoplatform/ks/bench/wiki/MockForumService.java b/component/injector/src/test/java/org/exoplatform/ks/bench/wiki/MockForumService.java index cf81b7fc7..385917c2e 100644 --- a/component/injector/src/test/java/org/exoplatform/ks/bench/wiki/MockForumService.java +++ b/component/injector/src/test/java/org/exoplatform/ks/bench/wiki/MockForumService.java @@ -267,6 +267,13 @@ public Topic getTopic(String categoryId, String forumId, String topicId, String public void setViewCountTopic(String path, String userRead) { } + + /* (non-Javadoc) + * @see org.exoplatform.forum.service.ForumService#writeViews() + */ + @Override + public void writeViews() { + } /* (non-Javadoc) * @see org.exoplatform.forum.service.ForumService#getTopicByPath(java.lang.String, boolean) @@ -880,6 +887,14 @@ public void createUserProfile(User user) throws Exception { @Override public void updateTopicAccess(String userId, String topicId) { + } + + /* (non-Javadoc) + * @see org.exoplatform.forum.service.ForumService#writeReads() + */ + @Override + public void writeReads() { + } /* (non-Javadoc) diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/DataStorage.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/DataStorage.java index b5067350e..8f00ec4c1 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/DataStorage.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/DataStorage.java @@ -302,6 +302,12 @@ public interface DataStorage { // void updateDataImported() throws Exception; void updateTopicAccess(String userId, String topicId); + + /** + * write user access a topic + * @since 2.2.11 + */ + void writeReads(); void updateForumAccess(String userId, String forumId); @@ -382,6 +388,12 @@ public interface DataStorage { KSDataLocation getDataLocation(); void setViewCountTopic(String path, String userRead); + + /** + * Write the number of topic viewers. + * @since 2.2.11 + */ + void writeViews(); JCRPageList getPostForSplitTopic(String topicPath) throws Exception; diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/ForumService.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/ForumService.java index abeef3647..61fdd0c32 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/ForumService.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/ForumService.java @@ -283,6 +283,12 @@ public interface ForumService extends ForumServiceLegacy { * @param userRead the user read */ void setViewCountTopic(String path, String userRead); + + /** + * Write the number of topic viewers. + * @since 2.2.11 + */ + void writeViews(); /** * Gets the topic by path. @@ -993,6 +999,12 @@ public interface ForumService extends ForumServiceLegacy { * @param topicId id of a topic */ void updateTopicAccess(String userId, String topicId); + + /** + * write user access a topic + * @since 2.2.11 + */ + void writeReads(); /** * update user access a forum diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java index e66735eff..9545217fc 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java @@ -1146,6 +1146,10 @@ public void importXML(String nodePath, ByteArrayInputStream bis, int typeImport) public void updateTopicAccess(String userId, String topicId) { storage.updateTopicAccess(userId, topicId); } + + public void writeReads() { + storage.writeReads(); + } public void updateForumAccess(String userId, String forumId) { storage.updateForumAccess(userId, forumId); @@ -1298,6 +1302,10 @@ public KSDataLocation getDataLocation() { public void setViewCountTopic(String path, String userRead) { storage.setViewCountTopic(path, userRead); } + + public void writeViews() { + storage.writeViews(); + } public JCRPageList getPostForSplitTopic(String topicPath) throws Exception { return storage.getPostForSplitTopic(topicPath); @@ -1349,8 +1357,22 @@ public ListPostData execute() { } - public int getPostsCount(PostFilter filter) throws Exception { - return storage.getPostsCount(filter); + public int getPostsCount(final PostFilter filter) throws Exception { + + SimpleCacheKey key = new SimpleCacheKey("postsCount", filter.toString()); + + SimpleCacheData data = miscDataFuture.get(new ServiceContext() { + public SimpleCacheData execute() { + try { + return new SimpleCacheData(storage.getPostsCount(filter)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }, key); + + return data.build(); + } diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/conf/DelayWritesJob.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/conf/DelayWritesJob.java new file mode 100644 index 000000000..7bbf135cd --- /dev/null +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/conf/DelayWritesJob.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2003-2012 eXo Platform SAS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.exoplatform.forum.service.conf; + +import org.exoplatform.forum.service.ForumService; +import org.exoplatform.ks.common.job.MultiTenancyJob; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.quartz.JobExecutionContext; + +/** + * @author Alain Defrance + */ +public class DelayWritesJob extends MultiTenancyJob { + + private static Log log_ = ExoLogger.getLogger(DelayWritesJob.class); + + @Override + public Class getTask() { + return UpdateViewTask.class; + } + + public class UpdateViewTask extends MultiTenancyTask { + + public UpdateViewTask(JobExecutionContext context, String repoName) { + super(context, repoName); + } + + @Override + public void run() { + super.run(); + try { + ForumService forumService = (ForumService) container.getComponentInstanceOfType(ForumService.class); + forumService.writeViews(); + forumService.writeReads(); + } catch (Exception e) { + log_.warn("Update view can not execute ..."); + } + if (log_.isDebugEnabled()) { + log_.info("\n\nUpdate view has been updated by a period login job"); + } + } + } + +} diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/ForumServiceImpl.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/ForumServiceImpl.java index a2c15b072..a7279959e 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/ForumServiceImpl.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/ForumServiceImpl.java @@ -434,6 +434,13 @@ public Topic getTopic(String categoryId, String forumId, String topicId, String public void setViewCountTopic(String path, String userRead){ storage.setViewCountTopic(path, userRead); } + + /** + * {@inheritDoc} + */ + public void writeViews() { + storage.writeViews(); + } /** * {@inheritDoc} @@ -1086,6 +1093,13 @@ public void evaluateActiveUsers(String query) { public void updateTopicAccess(String userId, String topicId) { storage.updateTopicAccess(userId, topicId); } + + /** + * {@inheritDoc} + */ + public void writeReads() { + storage.writeReads(); + } /** * {@inheritDoc} diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/JCRDataStorage.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/JCRDataStorage.java index f796c330d..326ad3251 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/JCRDataStorage.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/JCRDataStorage.java @@ -36,6 +36,7 @@ import java.util.Map.Entry; import java.util.Queue; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -174,6 +175,10 @@ public class JCRDataStorage implements DataStorage, ForumNodeTypes { private List defaultPlugins = new ArrayList(); + private Map updatingView = new ConcurrentHashMap(); + + private Map> updatingRead = new ConcurrentHashMap>(); + private List dataPlugins = new ArrayList(); private Map listeners = new HashMap(); @@ -1897,24 +1902,45 @@ public List getTopics(String categoryId, String forumId) throws Exception return topics; } - public void setViewCountTopic(String path, String userRead){ + public void setViewCountTopic(String path, String userRead) { + + if (userRead != null && userRead.length() > 0 && !userRead.equals(UserProfile.USER_GUEST)) { + if (updatingView.containsKey(path)) { + int value = updatingView.get((path)); + updatingView.put(path, value + 1); + } else { + updatingView.put(path, 1); + } + } + + } + + public void writeViews() { + // + Map map = updatingView; + updatingView = new ConcurrentHashMap(); + + // SessionProvider sProvider = CommonUtils.createSystemProvider(); - try { - Node topicNode = getCategoryHome(sProvider).getNode(path); - if (userRead != null && userRead.length() > 0 && !userRead.equals(UserProfile.USER_GUEST)) { - long newViewCount = new PropertyReader(topicNode).l(EXO_VIEW_COUNT) + 1; + + for (Map.Entry entry : map.entrySet()) { + + try { + Node topicNode = getCategoryHome(sProvider).getNode(entry.getKey()); + long newViewCount = new PropertyReader(topicNode).l(EXO_VIEW_COUNT) + entry.getValue(); topicNode.setProperty(EXO_VIEW_COUNT, newViewCount); if (topicNode.isNew()) { topicNode.getSession().save(); } else { topicNode.save(); } - } - } catch (Exception e) { - if (log.isDebugEnabled()) { - log.debug(String.format("Failed to set view number for topic with path %s", path), e); + } catch (Exception e) { + if (log.isDebugEnabled()) { + log.debug(String.format("Failed to set view number for topic with path %s", entry.getKey()), e); + } } } + } public Topic getTopic(String categoryId, String forumId, String topicId, String userRead) throws Exception { @@ -6688,36 +6714,66 @@ private void copyFullNodes(Node sourceNode, Node importNode, Session session) th } public void updateTopicAccess(String userId, String topicId){ + + if (updatingRead.containsKey(userId)) { + updatingRead.get(userId).add(topicId); + } else { + List value = new ArrayList(); + value.add(topicId); + updatingRead.put(userId, value); + } + + } + + public void writeReads() { + + // + Map> map = updatingRead; + updatingRead = new ConcurrentHashMap>(); + + // SessionProvider sysSession = CommonUtils.createSystemProvider(); - try { - if (!getUserProfileHome(sysSession).hasNode(userId)) { - return; - } - Node profile = getUserProfileHome(sysSession).getNode(userId); - List values = new ArrayList(); - if (profile.hasProperty(EXO_READ_TOPIC)) { - values = Utils.valuesToList(profile.getProperty(EXO_READ_TOPIC).getValues()); - } - int i = 0; - boolean isUpdated = false; - for (String vl : values) { - if (vl.indexOf(topicId) == 0) { - values.set(i, topicId + ":" + getGreenwichMeanTime().getTimeInMillis()); - isUpdated = true; - break; + + for (Map.Entry> entry : map.entrySet()) { + + try { + if (!getUserProfileHome(sysSession).hasNode(entry.getKey())) { + return; + } + + // + Node profile = getUserProfileHome(sysSession).getNode(entry.getKey()); + List values = new PropertyReader(profile).list(EXO_READ_TOPIC, Collections.EMPTY_LIST); + + // + for (String topicId : entry.getValue()) { + + // + int i = 0; + boolean isUpdated = false; + for (String vl : values) { + if (vl.indexOf(topicId) == 0) { + values.set(i, topicId + ":" + getGreenwichMeanTime().getTimeInMillis()); + isUpdated = true; + break; + } + i++; + } + + // + if (!isUpdated) { + values.add(topicId + ":" + getGreenwichMeanTime().getTimeInMillis()); + } + + profile.setProperty(EXO_READ_TOPIC, values.toArray(new String[values.size()])); + profile.save(); } - i++; - } - if (!isUpdated) { - values.add(topicId + ":" + getGreenwichMeanTime().getTimeInMillis()); + + } catch (Exception e) { + logDebug(String.format("Failed to update user %s acess for topic %s", entry.getKey(), "bar"), e); } - if (values.size() == 2 && Utils.isEmpty(values.get(0))) - values.remove(0); - profile.setProperty(EXO_READ_TOPIC, values.toArray(new String[values.size()])); - profile.save(); - } catch (Exception e) { - logDebug(String.format("Failed to update user %s acess for topic %s", userId, topicId), e); } + } public void updateForumAccess(String userId, String forumId){ diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/PostFilter.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/PostFilter.java index 0221186e6..6a69692bf 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/PostFilter.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/impl/model/PostFilter.java @@ -77,6 +77,17 @@ public String getUserLogin() { return userLogin; } - - + @Override + public String toString() { + return "PostFilter{" + + "categoryId='" + categoryId + '\'' + + ", forumId='" + forumId + '\'' + + ", topicId='" + topicId + '\'' + + ", isApproved='" + isApproved + '\'' + + ", isWaiting='" + isWaiting + '\'' + + ", isHidden='" + isHidden + '\'' + + ", userLogin='" + userLogin + '\'' + + ", topicPath='" + topicPath + '\'' + + '}'; + } } \ No newline at end of file diff --git a/eXoApplication/forum/webapp/src/test/java/org/exoplatform/forum/service/FakeForumService.java b/eXoApplication/forum/webapp/src/test/java/org/exoplatform/forum/service/FakeForumService.java index d17d61449..00c37ea80 100644 --- a/eXoApplication/forum/webapp/src/test/java/org/exoplatform/forum/service/FakeForumService.java +++ b/eXoApplication/forum/webapp/src/test/java/org/exoplatform/forum/service/FakeForumService.java @@ -668,6 +668,9 @@ public void setDefaultAvatar(String userName) { public void setViewCountTopic(String path, String userRead) { } + + public void writeViews() { + } public void unRegisterListenerForCategory(String path) throws Exception { } @@ -692,6 +695,9 @@ public void updateStatisticCounts(long topicCoutn, long postCount) throws Except public void updateTopicAccess(String userId, String topicId) { } + + public void writeReads() { + } public void updateUserProfile(User user) throws Exception { } diff --git a/extension/webapp/src/main/webapp/WEB-INF/ks-extension/ks/forum/cache-configuration.xml b/extension/webapp/src/main/webapp/WEB-INF/ks-extension/ks/forum/cache-configuration.xml index 52f0cce59..c429d3458 100644 --- a/extension/webapp/src/main/webapp/WEB-INF/ks-extension/ks/forum/cache-configuration.xml +++ b/extension/webapp/src/main/webapp/WEB-INF/ks-extension/ks/forum/cache-configuration.xml @@ -87,16 +87,28 @@ org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache - + + forum.PostData - forum.TopicList - ${cache.exo.forum.TopicList.Capacity} - ${cache.exo.forum.TopicList.TimeToLive} + forum.PostData + ${cache.exo.forum.PostData.Capacity:3000} + ${cache.exo.forum.PostData.TimeToLive:-1} org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache - --> + + + forum.PostList + + + forum.PostList + ${cache.exo.forum.PostList.Capacity:150} + ${cache.exo.forum.PostList.TimeToLive:86400} + org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache + + @@ -128,7 +140,7 @@ forum.ObjectNameData - ${cache.exo.forum.ObjectNameData.Capacity:500} + ${cache.exo.forum.ObjectNameData.Capacity:1800} ${cache.exo.forum.ObjectNameData.TimeToLive:-1} org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache @@ -141,7 +153,7 @@ forum.MiscData ${cache.exo.forum.MiscData.Capacity:600} - ${cache.exo.forum.MiscData.TimeToLive:-1} + ${cache.exo.forum.MiscData.TimeToLive:86400} org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache @@ -150,4 +162,4 @@ - + \ No newline at end of file diff --git a/extension/webapp/src/main/webapp/WEB-INF/ks-extension/ks/ks-configuration.xml b/extension/webapp/src/main/webapp/WEB-INF/ks-extension/ks/ks-configuration.xml index f26334608..03ee8f3e5 100644 --- a/extension/webapp/src/main/webapp/WEB-INF/ks-extension/ks/ks-configuration.xml +++ b/extension/webapp/src/main/webapp/WEB-INF/ks-extension/ks/ks-configuration.xml @@ -131,6 +131,28 @@ + + + + DelayWritesJob + addPeriodJob + org.exoplatform.services.scheduler.PeriodJob + update view count + + + job.info + save view count + + + + + + + + + + + From ce02028ee4a0f8599fc313f6e3945c67f6806b34 Mon Sep 17 00:00:00 2001 From: Alain Defrance Date: Mon, 1 Oct 2012 14:51:00 +0200 Subject: [PATCH 4/6] KS-4619 | Invalidate post count --- .../forum/service/cache/CachedDataStorage.java | 7 ++++++- .../forum/service/cache/model/CacheType.java | 1 + .../ks-extension/ks/forum/cache-configuration.xml | 10 ++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java index 9545217fc..4cb700f32 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java @@ -97,6 +97,7 @@ public class CachedDataStorage implements DataStorage, Startable { private ExoCache forumList; private ExoCache postData; private ExoCache postList; + private ExoCache postListCount; private ExoCache topicData; private ExoCache watchListData; private ExoCache linkListData; @@ -110,6 +111,7 @@ public class CachedDataStorage implements DataStorage, Startable { private FutureExoCache> forumListFuture; private FutureExoCache> postDataFuture; private FutureExoCache> postListFuture; + private FutureExoCache> postListCountFuture; private FutureExoCache> topicDataFuture; private FutureExoCache> watchListDataFuture; private FutureExoCache> linkListDataFuture; @@ -147,6 +149,7 @@ private void clearForumListCache() throws Exception { private void clearPostListCache() throws Exception { postList.select(new ScopeCacheSelector()); + postListCount.select(new ScopeCacheSelector()); } private void clearLinkListCache() throws Exception { @@ -222,6 +225,7 @@ public void start() { this.forumList = CacheType.FORUM_LIST.getFromService(service); this.postData = CacheType.POST_DATA.getFromService(service); this.postList = CacheType.POST_LIST.getFromService(service); + this.postListCount = CacheType.POST_LIST_COUNT.getFromService(service); this.topicData = CacheType.TOPIC_DATA.getFromService(service); this.objectNameData = CacheType.OBJECT_NAME_DATA.getFromService(service); this.miscData = CacheType.MISC_DATA.getFromService(service); @@ -235,6 +239,7 @@ public void start() { this.forumListFuture = CacheType.FORUM_LIST.createFutureCache(forumList); this.postDataFuture = CacheType.POST_DATA.createFutureCache(postData); this.postListFuture = CacheType.POST_LIST.createFutureCache(postList); + this.postListCountFuture = CacheType.POST_LIST_COUNT.createFutureCache(postListCount); this.topicDataFuture = CacheType.TOPIC_DATA.createFutureCache(topicData); this.watchListDataFuture = CacheType.WATCH_LIST_DATA.createFutureCache(watchListData); this.linkListDataFuture = CacheType.LINK_LIST_DATA.createFutureCache(linkListData); @@ -1361,7 +1366,7 @@ public int getPostsCount(final PostFilter filter) throws Exception { SimpleCacheKey key = new SimpleCacheKey("postsCount", filter.toString()); - SimpleCacheData data = miscDataFuture.get(new ServiceContext() { + SimpleCacheData data = postListCountFuture.get(new ServiceContext() { public SimpleCacheData execute() { try { return new SimpleCacheData(storage.getPostsCount(filter)); diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/CacheType.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/CacheType.java index aa715eead..fe8c05ea6 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/CacheType.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/CacheType.java @@ -20,6 +20,7 @@ public enum CacheType { TOPIC_LIST("forum.TopicList"), POST_DATA("forum.PostData"), POST_LIST("forum.PostList"), + POST_LIST_COUNT("forum.PostListCount"), WATCH_LIST_DATA("forum.WatchListData"), LINK_LIST_DATA("forum.LinkListData"), OBJECT_NAME_DATA("forum.ObjectNameData"), diff --git a/extension/webapp/src/main/webapp/WEB-INF/ks-extension/ks/forum/cache-configuration.xml b/extension/webapp/src/main/webapp/WEB-INF/ks-extension/ks/forum/cache-configuration.xml index c429d3458..9bc1c494e 100644 --- a/extension/webapp/src/main/webapp/WEB-INF/ks-extension/ks/forum/cache-configuration.xml +++ b/extension/webapp/src/main/webapp/WEB-INF/ks-extension/ks/forum/cache-configuration.xml @@ -109,6 +109,16 @@ org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache + + forum.PostListCount + + + forum.PostListCount + ${cache.exo.forum.PostListCount.Capacity:150} + ${cache.exo.forum.PostListCount.TimeToLive:-1} + org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache + + From 55856bcf3dd598d02056d7fe58f58bf1c566c2a2 Mon Sep 17 00:00:00 2001 From: thanhvc Date: Tue, 2 Oct 2012 10:30:45 +0700 Subject: [PATCH 5/6] Removes post list count by TopicId --- .../service/cache/CachedDataStorage.java | 32 +++++++-- .../cache/model/key/PostListCountKey.java | 66 +++++++++++++++++++ .../model/selector/PostListCountSelector.java | 57 ++++++++++++++++ 3 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/key/PostListCountKey.java create mode 100644 eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/selector/PostListCountSelector.java diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java index 4cb700f32..c7de79798 100644 --- a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/CachedDataStorage.java @@ -59,11 +59,13 @@ import org.exoplatform.forum.service.cache.model.key.LinkListKey; import org.exoplatform.forum.service.cache.model.key.ObjectNameKey; import org.exoplatform.forum.service.cache.model.key.PostKey; +import org.exoplatform.forum.service.cache.model.key.PostListCountKey; import org.exoplatform.forum.service.cache.model.key.PostListKey; import org.exoplatform.forum.service.cache.model.key.SimpleCacheKey; import org.exoplatform.forum.service.cache.model.key.TopicKey; import org.exoplatform.forum.service.cache.model.selector.CategoryIdSelector; import org.exoplatform.forum.service.cache.model.selector.ForumPathSelector; +import org.exoplatform.forum.service.cache.model.selector.PostListCountSelector; import org.exoplatform.forum.service.cache.model.selector.ScopeCacheSelector; import org.exoplatform.forum.service.impl.JCRDataStorage; import org.exoplatform.forum.service.impl.model.PostFilter; @@ -97,7 +99,7 @@ public class CachedDataStorage implements DataStorage, Startable { private ExoCache forumList; private ExoCache postData; private ExoCache postList; - private ExoCache postListCount; + private ExoCache postListCount; private ExoCache topicData; private ExoCache watchListData; private ExoCache linkListData; @@ -111,7 +113,7 @@ public class CachedDataStorage implements DataStorage, Startable { private FutureExoCache> forumListFuture; private FutureExoCache> postDataFuture; private FutureExoCache> postListFuture; - private FutureExoCache> postListCountFuture; + private FutureExoCache> postListCountFuture; private FutureExoCache> topicDataFuture; private FutureExoCache> watchListDataFuture; private FutureExoCache> linkListDataFuture; @@ -149,7 +151,10 @@ private void clearForumListCache() throws Exception { private void clearPostListCache() throws Exception { postList.select(new ScopeCacheSelector()); - postListCount.select(new ScopeCacheSelector()); + } + + private void clearPostListCountCache(String topicId) throws Exception { + postListCount.select(new PostListCountSelector(topicId)); } private void clearLinkListCache() throws Exception { @@ -747,8 +752,6 @@ public PostData execute() { public JCRPageList getListPostsByIP(String ip, String strOrderBy) throws Exception { return storage.getListPostsByIP(ip, strOrderBy); } - - public void savePost(String categoryId, String forumId, String topicId, Post post, boolean isNew, MessageBuilder messageBuilder) throws Exception { storage.savePost(categoryId, forumId, topicId, post, isNew, messageBuilder); @@ -756,9 +759,12 @@ public void savePost(String categoryId, String forumId, String topicId, Post pos clearForumListCache(); clearTopicCache(categoryId, forumId, topicId); clearPostListCache(); + // if (isNew == false) { clearPostCache(categoryId, forumId, topicId, post.getId()); + } else { + clearPostListCountCache(topicId); } statistic = null; } @@ -772,6 +778,7 @@ public void modifyPost(List posts, int type) { for(Post post : posts) { String categoryId = Utils.getCategoryId(post.getPath()); clearPostCache(categoryId, post.getForumId(), post.getTopicId(), post.getId()); + clearPostListCountCache(post.getTopicId()); } } catch (Exception e) { LOG.error(e.getMessage(), e); @@ -785,6 +792,7 @@ public Post removePost(String categoryId, String forumId, String topicId, String clearTopicCache(categoryId, forumId, topicId); clearPostListCache(); clearPostCache(categoryId, forumId, topicId, postId); + clearPostListCountCache(topicId); statistic = null; } catch (Exception e) { LOG.error(e.getMessage(), e); @@ -1319,11 +1327,21 @@ public JCRPageList getPostForSplitTopic(String topicPath) throws Exception { public void movePost(String[] postPaths, String destTopicPath, boolean isCreatNewTopic, String mailContent, String link) throws Exception { forumData.select(new ForumPathSelector(new String[] {Utils.getForumPath(postPaths[0]), Utils.getForumPath(destTopicPath)}, forumData)); clearForumListCache(); + String srcTopicPath = null; + for (String postPath : postPaths) { - clearTopicCache(postPath.substring(0, postPath.lastIndexOf("/"))); + srcTopicPath = postPath.substring(0, postPath.lastIndexOf("/")); + clearTopicCache(srcTopicPath); + // + Topic topic = getTopicByPath(srcTopicPath, false); + clearPostListCountCache(topic.getId()); } storage.movePost(postPaths, destTopicPath, isCreatNewTopic, mailContent, link); clearTopicCache(destTopicPath); + //clear PostListCount caching. + Topic topic = getTopicByPath(destTopicPath, false); + clearPostListCountCache(topic.getId()); + clearPostListCache(); } public void mergeTopic(String srcTopicPath, String destTopicPath, String mailContent, String link) throws Exception { @@ -1364,7 +1382,7 @@ public ListPostData execute() { public int getPostsCount(final PostFilter filter) throws Exception { - SimpleCacheKey key = new SimpleCacheKey("postsCount", filter.toString()); + PostListCountKey key = new PostListCountKey("postsCount", filter.toString(), filter.getTopicId()); SimpleCacheData data = postListCountFuture.get(new ServiceContext() { public SimpleCacheData execute() { diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/key/PostListCountKey.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/key/PostListCountKey.java new file mode 100644 index 000000000..9419a6699 --- /dev/null +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/key/PostListCountKey.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2003-2012 eXo Platform SAS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.exoplatform.forum.service.cache.model.key; + +import org.exoplatform.forum.service.cache.model.ScopeCacheKey; + +/** + * Created by The eXo Platform SAS + * Author : thanh_vucong + * thanh_vucong@exoplatform.com + * Oct 2, 2012 + */ +public class PostListCountKey extends ScopeCacheKey { + + private final String type; + private final String key; + private final String topicId; + + public PostListCountKey(String type, String key, String topicId) { + this.type = type; + this.key = key; + this.topicId = topicId; + } + + public String getTopicId() { + return topicId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PostListCountKey)) return false; + if (!super.equals(o)) return false; + + PostListCountKey that = (PostListCountKey) o; + + if (key != null ? !key.equals(that.key) : that.key != null) return false; + if (type != null ? !type.equals(that.type) : that.type != null) return false; + if (topicId != null ? !topicId.equals(that.topicId) : that.topicId != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (type != null ? type.hashCode() : 0); + result = 31 * result + (key != null ? key.hashCode() : 0); + result = 31 * result + (topicId != null ? topicId.hashCode() : 0); + return result; + } +} diff --git a/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/selector/PostListCountSelector.java b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/selector/PostListCountSelector.java new file mode 100644 index 000000000..69cd2605b --- /dev/null +++ b/eXoApplication/forum/service/src/main/java/org/exoplatform/forum/service/cache/model/selector/PostListCountSelector.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2003-2012 eXo Platform SAS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.exoplatform.forum.service.cache.model.selector; + +import org.exoplatform.forum.service.cache.model.ScopeCacheKey; +import org.exoplatform.forum.service.cache.model.key.PostListCountKey; +import org.exoplatform.services.cache.ObjectCacheInfo; + + +/** + * Created by The eXo Platform SAS + * Author : thanh_vucong + * thanh_vucong@exoplatform.com + * Oct 2, 2012 + */ +public class PostListCountSelector extends ScopeCacheSelector { + + private String topicId; + + public PostListCountSelector(final String topicId) { + + if (topicId == null) { + throw new NullPointerException(); + } + + this.topicId = topicId; + } + + @Override + public boolean select(final ScopeCacheKey key, final ObjectCacheInfo ocinfo) { + if (!super.select(key, ocinfo)) { + return false; + } + + if (key instanceof PostListCountKey) { + return ((PostListCountKey)key).getTopicId().equals(this.topicId); + } + + return false; + } + +} + From d8da669583e626b184e033e24474b81a2ffb169d Mon Sep 17 00:00:00 2001 From: thanhvc Date: Tue, 2 Oct 2012 18:06:50 +0700 Subject: [PATCH 6/6] KS-4619 | Add UT of Caching for both getPosts() and getPostsCount() --- .../service/cache/TestCacheDataStrorage.java | 400 ++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 eXoApplication/forum/service/src/test/java/org/exoplatform/forum/service/cache/TestCacheDataStrorage.java diff --git a/eXoApplication/forum/service/src/test/java/org/exoplatform/forum/service/cache/TestCacheDataStrorage.java b/eXoApplication/forum/service/src/test/java/org/exoplatform/forum/service/cache/TestCacheDataStrorage.java new file mode 100644 index 000000000..4e63b0346 --- /dev/null +++ b/eXoApplication/forum/service/src/test/java/org/exoplatform/forum/service/cache/TestCacheDataStrorage.java @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2003-2012 eXo Platform SAS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.exoplatform.forum.service.cache; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.Session; + +import org.exoplatform.commons.utils.PropertyManager; +import org.exoplatform.container.ExoContainer; +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.container.RootContainer; +import org.exoplatform.container.StandaloneContainer; +import org.exoplatform.container.util.ContainerUtil; +import org.exoplatform.forum.service.Category; +import org.exoplatform.forum.service.DataStorage; +import org.exoplatform.forum.service.Forum; +import org.exoplatform.forum.service.MessageBuilder; +import org.exoplatform.forum.service.Post; +import org.exoplatform.forum.service.Topic; +import org.exoplatform.forum.service.Utils; +import org.exoplatform.forum.service.impl.model.PostFilter; +import org.exoplatform.ks.common.jcr.JCRSessionManager; +import org.exoplatform.ks.common.jcr.KSDataLocation; +import org.exoplatform.services.jcr.RepositoryService; +import org.exoplatform.services.jcr.ext.app.SessionProviderService; +import org.exoplatform.services.jcr.ext.common.SessionProvider; +import org.exoplatform.services.jcr.util.IdGenerator; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.exoplatform.services.security.ConversationState; +import org.exoplatform.services.security.Identity; +import org.exoplatform.test.BasicTestCase; + +/** + * Created by The eXo Platform SAS + * Author : thanh_vucong + * thanh_vucong@exoplatform.com + * Oct 2, 2012 + */ +public class TestCacheDataStrorage extends BasicTestCase { + + protected static Log log = ExoLogger.getLogger("sample.services.test"); + + protected static RepositoryService repositoryService; + + protected static StandaloneContainer container; + + protected final static String REPO_NAME = "repository".intern(); + + protected final static String SYSTEM_WS = "system".intern(); + + protected final static String KNOWLEDGE_WS = "knowledge".intern(); + + protected static Node root_ = null; + + protected SessionProvider sProvider; + + private static SessionProviderService sessionProviderService = null; + + static { + // we do this in static to save a few cycles + initContainer(); + initJCR(); + } + + private DataStorage cacheDataStorage; + + private KSDataLocation dataLocation; + + private String categoryId; + + private String forumId; + + private String topicId; + + public TestCacheDataStrorage() throws Exception { + } + + public void setUp() throws Exception { + startSystemSession(); + cacheDataStorage = (CachedDataStorage) container.getComponentInstanceOfType(DataStorage.class); + dataLocation = (KSDataLocation) container.getComponentInstanceOfType(KSDataLocation.class); + SessionProviderService sessionProviderService = (SessionProviderService) container.getComponentInstanceOfType(SessionProviderService.class); + sProvider = sessionProviderService.getSystemSessionProvider(null); + + // + setData(); + } + + public void tearDown() throws Exception { + super.tearDown(); + killData(); + + } + + public void testPostListAccess() throws Exception { + // set Data + setData(); + + //create 26 posts + List posts = new ArrayList(); + for (int i = 0; i < 25; ++i) { + Post post = createdPost(); + posts.add(post); + cacheDataStorage.savePost(categoryId, forumId, topicId, post, true, new MessageBuilder()); + } + // getPost + assertNotNull(cacheDataStorage.getPost(categoryId, forumId, topicId, posts.get(0).getId())); + assertEquals(25, cacheDataStorage.getTopic(categoryId, forumId, topicId, "").getPostCount()); + + // get Page 1 + List gotList = cacheDataStorage.getPosts(new PostFilter(categoryId, forumId, topicId, "", "", "", "root"), 0, 10); + assertEquals(10, gotList.size());// size = 10: page 1 + + + //Page 2 + gotList = cacheDataStorage.getPosts(new PostFilter(categoryId, forumId, topicId, "", "", "", "root"), 10, 10); + assertEquals(10, gotList.size());// size = 10: page 2 + + //Page 3 + gotList = cacheDataStorage.getPosts(new PostFilter(categoryId, forumId, topicId, "", "", "", "root"), 20, 10); + assertEquals(6, gotList.size());// size = 6: page 2 + } + + + public void testPostListCount() throws Exception { + // set Data + setData(); + + //create 25 + 1 post default post when created topic + List posts = new ArrayList(); + for (int i = 0; i < 25; ++i) { + Post post = createdPost(); + posts.add(post); + cacheDataStorage.savePost(categoryId, forumId, topicId, post, true, new MessageBuilder()); + } + // getPost + assertNotNull(cacheDataStorage.getPost(categoryId, forumId, topicId, posts.get(0).getId())); + + //isApproved = true + assertEquals(26, cacheDataStorage.getPostsCount(new PostFilter(this.categoryId, this.forumId, topicId, "true", "false", "false", "root"))); + + //isApproved = false + assertEquals(0, cacheDataStorage.getPostsCount(new PostFilter(this.categoryId, this.forumId, topicId, "false", "false", "false", "root"))); + + //isHidden = true + assertEquals(0, cacheDataStorage.getPostsCount(new PostFilter(this.categoryId, this.forumId, topicId, "false", "true", "false", "root"))); + //isWaiting = true + assertEquals(0, cacheDataStorage.getPostsCount(new PostFilter(this.categoryId, this.forumId, topicId, "false", "false", "true", "root"))); + + { //add more posts + //add more 25 posts + for (int i = 0; i < 25; ++i) { + Post post = createdPost(); + posts.add(post); + cacheDataStorage.savePost(categoryId, forumId, topicId, post, true, new MessageBuilder()); + } + + //isApproved = true + assertEquals(51, cacheDataStorage.getPostsCount(new PostFilter(this.categoryId, this.forumId, topicId, "true", "false", "false", "root"))); + + //isApproved = false + assertEquals(0, cacheDataStorage.getPostsCount(new PostFilter(this.categoryId, this.forumId, topicId, "false", "false", "false", "root"))); + + //isHidden = true + assertEquals(0, cacheDataStorage.getPostsCount(new PostFilter(this.categoryId, this.forumId, topicId, "false", "true", "false", "root"))); + //isWaiting = true + assertEquals(0, cacheDataStorage.getPostsCount(new PostFilter(this.categoryId, this.forumId, topicId, "false", "false", "true", "root"))); + + } + } + + + protected void startSystemSession() { + sProvider = sessionProviderService.getSystemSessionProvider(null); + } + + protected void startSessionAs(String user) { + Identity identity = new Identity(user); + ConversationState state = new ConversationState(identity); + sessionProviderService.setSessionProvider(null, new SessionProvider(state)); + sProvider = sessionProviderService.getSessionProvider(null); + } + + protected void endSession() { + sessionProviderService.removeSessionProvider(null); + startSystemSession(); + } + + /** + * All elements of a list should be contained in the expected array of String + * @param message + * @param expected + * @param actual + */ + public static void assertContainsAll(String message, List expected, List actual) { + assertEquals(message, expected.size(), actual.size()); + assertTrue(message, expected.containsAll(actual)); + } + + /** + * Assertion method on string arrays + * @param message + * @param expected + * @param actual + */ + public static void assertEquals(String message, String[] expected, String[] actual) { + assertEquals(message, expected.length, actual.length); + for (int i = 0; i < expected.length; i++) { + assertEquals(message, expected[i], actual[i]); + } + } + + private static void initContainer() { + //ClassLoader parentLoader; + + initProperties(); + + try { + ExoContainer container_ = RootContainer.getInstance(); + if (container_ != null) { + container_.stop(); + container_.dispose(); + } + String containerConf = TestCacheDataStrorage.class.getResource("/conf/portal/configuration.xml").toString(); + StandaloneContainer.addConfigurationURL(containerConf); + + container = StandaloneContainer.getInstance(); + String loginConf = Thread.currentThread().getContextClassLoader().getResource("conf/portal/login.conf").toString(); + + if (System.getProperty("java.security.auth.login.config") == null) + System.setProperty("java.security.auth.login.config", loginConf); + ExoContainerContext.setCurrentContainer(container); + } catch (Exception e) { + log.error("Failed to initialize standalone container: ", e); + } finally { + //Thread.currentThread().setContextClassLoader(parentLoader); + } + } + + private static void initProperties() { + // + URL fileProps = TestCacheDataStrorage.class.getResource("/conf/portal/configuration.properties"); + Map props = ContainerUtil.loadProperties(fileProps); + if (props != null) + { + for (Map.Entry entry : props.entrySet()) + { + String propertyName = entry.getKey(); + String propertyValue = entry.getValue(); + PropertyManager.setProperty(propertyName, propertyValue); + } + } + } + + private static void initJCR() { + try { + repositoryService = (RepositoryService) container.getComponentInstanceOfType(RepositoryService.class); + + // Initialize datas + Session session = repositoryService.getRepository(REPO_NAME).getSystemSession(KNOWLEDGE_WS); + root_ = session.getRootNode(); + sessionProviderService = (SessionProviderService) container.getComponentInstanceOfType(SessionProviderService.class); + + JCRSessionManager sessionManager = new JCRSessionManager(KNOWLEDGE_WS, repositoryService); + KSDataLocation ksDataLocation = (KSDataLocation) container.getComponentInstanceOfType(KSDataLocation.class); + ksDataLocation.setSessionManager(sessionManager); + } catch (Exception e) { + throw new RuntimeException("Failed to initialize JCR: ", e); + } + } + + private void setData() throws Exception { + + Category cat = createCategory(getId(Utils.CATEGORY)); + this.categoryId = cat.getId(); + cacheDataStorage.saveCategory(cat, true); + Forum forum = createdForum(); + this.forumId = forum.getId(); + cacheDataStorage.saveForum(categoryId, forum, true); + Topic topic = createdTopic("root"); + cacheDataStorage.saveTopic(categoryId, forumId, topic, true, false, new MessageBuilder()); + this.topicId = topic.getId(); + } + + private void killData() throws Exception { + List cats = cacheDataStorage.getCategories(); + if (cats.size() > 0) { + for (Category category : cats) { + cacheDataStorage.removeCategory(category.getId()); + } + } + } + + private Post createdPost() { + Post post = new Post(); + post.setOwner("root"); + post.setCreatedDate(new Date()); + post.setModifiedBy("root"); + post.setModifiedDate(new Date()); + post.setName("SubJect"); + post.setMessage("content description"); + post.setRemoteAddr("192.168.1.11"); + post.setIcon("classNameIcon"); + post.setIsApproved(true); + post.setIsActiveByTopic(true); + post.setIsHidden(false); + post.setIsWaiting(false); + return post; + } + + private Topic createdTopic(String owner) { + Topic topicNew = new Topic(); + + topicNew.setOwner(owner); + topicNew.setTopicName("TestTopic"); + topicNew.setCreatedDate(new Date()); + topicNew.setModifiedBy("root"); + topicNew.setModifiedDate(new Date()); + topicNew.setLastPostBy("root"); + topicNew.setLastPostDate(new Date()); + topicNew.setDescription("Topic description"); + topicNew.setPostCount(0); + topicNew.setViewCount(0); + topicNew.setIsNotifyWhenAddPost(""); + topicNew.setIsModeratePost(false); + topicNew.setIsClosed(false); + topicNew.setIsLock(false); + topicNew.setIsWaiting(false); + topicNew.setIsActive(true); + topicNew.setIcon("classNameIcon"); + topicNew.setIsApproved(true); + topicNew.setCanView(new String[] {}); + topicNew.setCanPost(new String[] {}); + return topicNew; + } + + private Forum createdForum() { + Forum forum = new Forum(); + forum.setOwner("root"); + forum.setForumName("TestForum"); + forum.setForumOrder(1); + forum.setCreatedDate(new Date()); + forum.setModifiedBy("root"); + forum.setModifiedDate(new Date()); + forum.setLastTopicPath(""); + forum.setDescription("description"); + forum.setPostCount(0); + forum.setTopicCount(0); + + forum.setNotifyWhenAddTopic(new String[] {}); + forum.setNotifyWhenAddPost(new String[] {}); + forum.setIsModeratePost(false); + forum.setIsModerateTopic(false); + forum.setIsClosed(false); + forum.setIsLock(false); + + forum.setViewer(new String[] {}); + forum.setCreateTopicRole(new String[] {}); + forum.setModerators(new String[] {}); + return forum; + } + + private Category createCategory(String id) { + Category cat = new Category(id); + cat.setOwner("root"); + cat.setCategoryName("testCategory"); + cat.setCategoryOrder(1); + cat.setCreatedDate(new Date()); + cat.setDescription("desciption"); + cat.setModifiedBy("root"); + cat.setModifiedDate(new Date()); + return cat; + } + + private String getId(String type) { + return type + IdGenerator.generate(); + } + +} \ No newline at end of file