001/* 002 * Copyright 2011 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.blog; 017 018import java.util.ArrayList; 019import java.util.Calendar; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.Comparator; 023import java.util.Date; 024import java.util.HashMap; 025import java.util.HashSet; 026import java.util.Iterator; 027import java.util.LinkedHashMap; 028import java.util.List; 029import java.util.Map; 030import java.util.Map.Entry; 031import java.util.Set; 032import java.util.concurrent.TimeUnit; 033 034import org.apache.avalon.framework.activity.Initializable; 035import org.apache.avalon.framework.component.Component; 036import org.apache.avalon.framework.logger.AbstractLogEnabled; 037import org.apache.avalon.framework.service.ServiceException; 038import org.apache.avalon.framework.service.ServiceManager; 039import org.apache.avalon.framework.service.Serviceable; 040import org.apache.commons.lang.BooleanUtils; 041import org.joda.time.DateTime; 042 043import org.ametys.cms.ObservationConstants; 044import org.ametys.cms.repository.Content; 045import org.ametys.cms.repository.ContentLanguageExpression; 046import org.ametys.cms.repository.ContentTypeExpression; 047import org.ametys.cms.tag.Tag; 048import org.ametys.cms.tag.TagHelper; 049import org.ametys.cms.tag.TagProviderExtensionPoint; 050import org.ametys.core.observation.Event; 051import org.ametys.core.observation.ObservationManager; 052import org.ametys.core.user.UserIdentity; 053import org.ametys.plugins.blog.posts.PostContentType; 054import org.ametys.plugins.repository.AmetysObjectIterable; 055import org.ametys.plugins.repository.AmetysObjectResolver; 056import org.ametys.plugins.repository.metadata.CompositeMetadata; 057import org.ametys.plugins.repository.provider.WorkspaceSelector; 058import org.ametys.plugins.repository.query.QueryHelper; 059import org.ametys.plugins.repository.query.SortCriteria; 060import org.ametys.plugins.repository.query.expression.AndExpression; 061import org.ametys.plugins.repository.query.expression.Expression; 062import org.ametys.plugins.repository.query.expression.Expression.Operator; 063import org.ametys.plugins.repository.query.expression.MetadataExpression; 064import org.ametys.plugins.repository.query.expression.StringExpression; 065import org.ametys.web.WebConstants; 066import org.ametys.web.repository.content.jcr.DefaultWebContent; 067import org.ametys.web.repository.site.SiteManager; 068import org.ametys.web.site.SiteConfigurationExtensionPoint; 069 070import com.google.common.collect.Multimap; 071import com.google.common.collect.TreeMultimap; 072 073/** 074 * Blog cache manager. 075 */ 076public class BlogCacheManager extends AbstractLogEnabled implements Component, Serviceable, Initializable 077{ 078 /** The name of metadata for date of post */ 079 public static final String POST_METADATA_DATE = "date"; 080 081 /** The avalon role. */ 082 public static final String ROLE = BlogCacheManager.class.getName(); 083 084 /** The ametys object resolver. */ 085 protected AmetysObjectResolver _ametysResolver; 086 087 /** Th tag provider */ 088 protected TagProviderExtensionPoint _tagProviderEP; 089 090 /** The Site manager. */ 091 protected SiteManager _siteManager; 092 093 /** The site configuration extension point */ 094 protected SiteConfigurationExtensionPoint _siteConf; 095 096 /** The blog root page handler. */ 097 protected BlogPageHandler _blogRootHandler; 098 099 /** The workspace selector. */ 100 protected WorkspaceSelector _workspaceSelector; 101 102 /** The Ametys resolver */ 103 protected AmetysObjectResolver _resolver; 104 105 /** The observation manager */ 106 protected ObservationManager _observationManager; 107 108 /** Map of PostDateCache by workspace, site and sitemap/language. */ 109 protected Map<String, Map<String, Map<String, PostDateCache>>> _postsByDate; 110 111 /** Map of PostTagCache by workspace, site and sitemap/language. */ 112 protected Map<String, Map<String, Map<String, PostTagCache>>> _postsByTag; 113 114 /** Map of PostDateCache by workspace, site and sitemap/language. */ 115 protected Map<String, Map<String, Map<String, AllPostCache>>> _allPosts; 116 117 /** Map of FuturePostCache by workspace, site and sitemap/language. */ 118 protected Map<String, Map<String, Map<String, FuturePostCache>>> _futurePostsCache; 119 120 /** Caches are initialized by workspace, site and sitemap/language. */ 121 protected Map<String, Map<String, Map<String, Boolean>>> _initialized; 122 123 /** Future caches are updated by workspace, site and sitemap/language. */ 124 protected Map<String, Map<String, Map<String, Date>>> _lastUpdateFuturePosts; 125 126 @Override 127 public void service(ServiceManager serviceManager) throws ServiceException 128 { 129 _ametysResolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 130 _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE); 131 _siteConf = (SiteConfigurationExtensionPoint) serviceManager.lookup(SiteConfigurationExtensionPoint.ROLE); 132 _blogRootHandler = (BlogPageHandler) serviceManager.lookup(BlogPageHandler.ROLE); 133 _workspaceSelector = (WorkspaceSelector) serviceManager.lookup(WorkspaceSelector.ROLE); 134 _tagProviderEP = (TagProviderExtensionPoint) serviceManager.lookup(TagProviderExtensionPoint.ROLE); 135 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 136 _observationManager = (ObservationManager) serviceManager.lookup(ObservationManager.ROLE); 137 } 138 139 @Override 140 public void initialize() throws Exception 141 { 142 _postsByDate = new HashMap<>(); 143 _postsByTag = new HashMap<>(); 144 _allPosts = new HashMap<>(); 145 _futurePostsCache = new HashMap<>(); 146 _initialized = new HashMap<>(); 147 _lastUpdateFuturePosts = new HashMap<>(); 148 } 149 150 /** 151 * Get the years with posts. 152 * @param siteName the site name. 153 * @param language the language. 154 * @param year the year 155 * @return the years with posts as a Set of Integer. 156 */ 157 public boolean hasYear(String siteName, String language, int year) 158 { 159 PostDateCache cache = getPostDateCache(siteName, language); 160 161 return cache.hasYear(year); 162 } 163 164 /** 165 * Get the years with posts. 166 * @param siteName the site name. 167 * @param language the language. 168 * @return the years with posts as a Set of Integer. 169 */ 170 public Set<Integer> getYears(String siteName, String language) 171 { 172 PostDateCache cache = getPostDateCache(siteName, language); 173 174 return cache.getYears(); 175 } 176 177 /** 178 * Get the years with posts. 179 * @param siteName the site name. 180 * @param language the language. 181 * @param year the year 182 * @param month the month 183 * @return the years with posts as a Set of Integer. 184 */ 185 public boolean hasMonth(String siteName, String language, int year, int month) 186 { 187 PostDateCache cache = getPostDateCache(siteName, language); 188 189 return cache.hasMonth(year, month); 190 } 191 192 /** 193 * Get the months with posts for a specified year. 194 * @param siteName the site name. 195 * @param language the language. 196 * @param year the year. 197 * @return the months with posts in the specified year as a Set of Integer. 198 */ 199 public Set<Integer> getMonths(String siteName, String language, int year) 200 { 201 PostDateCache cache = getPostDateCache(siteName, language); 202 203 return cache.getMonths(year); 204 } 205 206 /** 207 * Get the posts for a year-month couple. 208 * @param siteName the site name. 209 * @param language the language. 210 * @param year the year. 211 * @param month the month. 212 * @return the posts in the specified year and month. 213 */ 214 public Map<String, Post> getPostsByMonth(String siteName, String language, int year, int month) 215 { 216 PostDateCache cache = getPostDateCache(siteName, language); 217 218 return cache.getPosts(year, month); 219 } 220 221 /** 222 * Get the posts for a year-month couple. 223 * @param siteName the site name. 224 * @param language the language. 225 * @param year the year. 226 * @param month the month. 227 * @return the posts in the specified year and month. 228 */ 229 public Collection<Post> getSortedPostsByMonth(String siteName, String language, int year, int month) 230 { 231 PostDateCache cache = getPostDateCache(siteName, language); 232 233 List<Post> sortedPosts = new ArrayList<>(cache.getPosts(year, month).values()); 234 235 Collections.sort(sortedPosts, new ReverseDatePostComparator()); 236 237 return sortedPosts; 238 } 239 240 /** 241 * Get the posts for a year. 242 * @param siteName the site name. 243 * @param language the language. 244 * @param year the year. 245 * @return the posts in the specified year. 246 */ 247 public Map<String, Post> getPostsByYear(String siteName, String language, int year) 248 { 249 PostDateCache cache = getPostDateCache(siteName, language); 250 251 return cache.getPosts(year); 252 } 253 254 /** 255 * Get the posts for a year. 256 * @param siteName the site name. 257 * @param language the language. 258 * @param year the year. 259 * @return the posts in the specified year. 260 */ 261 public Collection<Post> getSortedPostsByYear(String siteName, String language, int year) 262 { 263 PostDateCache cache = getPostDateCache(siteName, language); 264 265 List<Post> sortedPosts = new ArrayList<>(cache.getPosts(year).values()); 266 267 Collections.sort(sortedPosts, new ReverseDatePostComparator()); 268 269 return sortedPosts; 270 } 271 272 /** 273 * Get the posts for a year-month couple. 274 * @param siteName the site name. 275 * @param language the language. 276 * @param year the year. 277 * @param month the month. 278 * @param name the name 279 * @return the posts in the specified year and month. 280 */ 281 public Post getPostByName(String siteName, String language, int year, int month, String name) 282 { 283 PostDateCache cache = getPostDateCache(siteName, language); 284 285 return cache.getPostByName(year, month, name); 286 } 287 288 /** 289 * Get the posts for a year-month couple. 290 * @param siteName the site name. 291 * @param language the language. 292 * @param year the year. 293 * @param month the month. 294 * @param id the id of post 295 * @return the posts in the specified year and month. 296 */ 297 public boolean hasPost(String siteName, String language, int year, int month, String id) 298 { 299 PostDateCache cache = getPostDateCache(siteName, language); 300 301 return cache.hasPost(year, month, id); 302 } 303 304 /** 305 * Get the posts for a year-month couple. 306 * @param siteName the site name. 307 * @param language the language. 308 * @param year the year. 309 * @param month the month. 310 * @param name the name of post 311 * @return the posts in the specified year and month. 312 */ 313 public boolean hasPostByName(String siteName, String language, int year, int month, String name) 314 { 315 PostDateCache cache = getPostDateCache(siteName, language); 316 317 return cache.hasPostByName(year, month, name); 318 } 319 320 ///////////////////////////////////////////////// 321 /// POSTS BY TAG METHODS /// 322 ///////////////////////////////////////////////// 323 324 /** 325 * Get the tags for a site and language. 326 * @param siteName The site name 327 * @param language The language 328 * @return the tag list. 329 */ 330 public Set<String> getTags(String siteName, String language) 331 { 332 PostTagCache cache = getPostTagCache(siteName, language); 333 334 return cache.getTags(); 335 } 336 337 /** 338 * Test if a tag exists in the given site and language. 339 * @param siteName The site name 340 * @param language The language 341 * @param tagName The tag name 342 * @return true if the tag exists, false otherwise. 343 */ 344 public boolean hasTag(String siteName, String language, String tagName) 345 { 346 PostTagCache cache = getPostTagCache(siteName, language); 347 348 Map<String, Object> params = new HashMap<>(); 349 params.put("siteName", siteName); 350 Tag tag = _tagProviderEP.getTag(tagName, params); 351 Set<String> descendantNamesAndItSelf = TagHelper.getDescendantNames(tag, true); 352 353 for (String descendantName : descendantNamesAndItSelf) 354 { 355 if (cache.hasTag(descendantName)) 356 { 357 return true; 358 } 359 } 360 361 return false; 362 } 363 364 /** 365 * Get the posts for a given tag. 366 * @param siteName The site name 367 * @param language The language 368 * @param tagName The tag name 369 * @return the tag posts, indexed by ID. 370 */ 371 public Map<String, Post> getPostsByTag(String siteName, String language, String tagName) 372 { 373 PostTagCache cache = getPostTagCache(siteName, language); 374 375 return cache.getPosts(tagName); 376 } 377 378 /** 379 * Get the posts for a given tag. 380 * @param siteName The site name 381 * @param language The language 382 * @param tagName The tag name 383 * @return the tag posts, indexed by ID. 384 */ 385 public Collection<Post> getSortedPostsByTag(String siteName, String language, String tagName) 386 { 387 return getSortedPostsByTag (siteName, language, tagName, false); 388 } 389 390 /** 391 * Get the posts for a given tag. 392 * @param siteName The site name 393 * @param language the language 394 * @param tagName the tag name 395 * @param deepSearch true to perform deep search 396 * @return the tag posts, indexed by ID. 397 */ 398 public Collection<Post> getSortedPostsByTag(String siteName, String language, String tagName, boolean deepSearch) 399 { 400 PostTagCache cache = getPostTagCache(siteName, language); 401 402 List<Post> sortedPosts = new ArrayList<>(cache.getPosts(tagName).values()); 403 404 if (deepSearch) 405 { 406 Map<String, Object> contextualParams = new HashMap<>(); 407 contextualParams.put("siteName", siteName); 408 409 Tag tag = _tagProviderEP.getTag(tagName, contextualParams); 410 if (tag != null) 411 { 412 Set<String> descendantsNames = TagHelper.getDescendantNames(tag, false); 413 for (String descendantsName : descendantsNames) 414 { 415 sortedPosts.addAll(cache.getPosts(descendantsName).values()); 416 } 417 } 418 } 419 420 Collections.sort(sortedPosts, new ReverseDatePostComparator()); 421 422 return sortedPosts; 423 } 424 425 /** 426 * Test if a given post has a tag, by its ID. 427 * @param siteName The site name 428 * @param language The language 429 * @param tagName The tag name 430 * @param postId The id of post 431 * @return true if the given post has the given tag. 432 */ 433 public boolean hasPostByTag(String siteName, String language, String tagName, String postId) 434 { 435 PostTagCache cache = getPostTagCache(siteName, language); 436 437 return cache.hasPost(tagName, postId); 438 } 439 440 /** 441 * Get the posts for a year-month couple. 442 * @param siteName The site name 443 * @param language The language 444 * @param tagName The tag name 445 * @param id The id of post 446 * @return the posts in the specified year and month. 447 */ 448 public Post getPost(String siteName, String language, String tagName, String id) 449 { 450 PostTagCache cache = getPostTagCache(siteName, language); 451 452 return cache.getPost(tagName, id); 453 } 454 455 /** 456 * Get the posts for a year-month couple. 457 * @param siteName The site name 458 * @param language The language 459 * @param tagName The tag name 460 * @param name the name of post 461 * @return the posts in the specified year and month. 462 */ 463 public Post getPostByName(String siteName, String language, String tagName, String name) 464 { 465 PostTagCache cache = getPostTagCache(siteName, language); 466 467 return cache.getPostByName(tagName, name); 468 } 469 470 /** 471 * Get the posts for a year-month couple. 472 * @param siteName The site name 473 * @param language The language 474 * @param tagName The tag name 475 * @param id The id of post 476 * @return the posts in the specified year and month. 477 */ 478 public boolean hasPost(String siteName, String language, String tagName, String id) 479 { 480 PostTagCache cache = getPostTagCache(siteName, language); 481 482 return cache.hasPost(tagName, id); 483 } 484 485 /** 486 * Get the posts for a year-month couple. 487 * @param siteName The site name 488 * @param language The language 489 * @param tagName The tag name 490 * @param name the name of post 491 * @return the posts in the specified year and month. 492 */ 493 public boolean hasPostByName(String siteName, String language, String tagName, String name) 494 { 495 PostTagCache cache = getPostTagCache(siteName, language); 496 497 return cache.hasPostByName(tagName, name); 498 } 499 500 ///////////////////////////////////////////////// 501 /// ALL POSTS METHODS /// 502 ///////////////////////////////////////////////// 503 504 /** 505 * Get a list of all the posts' IDs. 506 * @param siteName the site name. 507 * @param language the language. 508 * @return all the posts' IDs as a Set. 509 */ 510 public Set<String> getPostIds(String siteName, String language) 511 { 512 AllPostCache cache = getAllPostCache(siteName, language); 513 514 return cache.getPostIds(); 515 } 516 517 /** 518 * Get the exhaustive list of Posts. 519 * @param siteName the site name. 520 * @param language the language. 521 * @return the exhaustive list of Posts, indexed by their ID. 522 */ 523 public Map<String, Post> getPosts(String siteName, String language) 524 { 525 AllPostCache cache = getAllPostCache(siteName, language); 526 527 return cache.getPosts(); 528 } 529 530 /** 531 * Get the exhaustive list of Posts. 532 * @param siteName the site name. 533 * @param language the language. 534 * @return the exhaustive list of Posts, indexed by their ID. 535 */ 536 public Collection<Post> getSortedPosts(String siteName, String language) 537 { 538 AllPostCache cache = getAllPostCache(siteName, language); 539 540 List<Post> sortedPosts = new ArrayList<>(cache.getPosts().values()); 541 542 Collections.sort(sortedPosts, new ReverseDatePostComparator()); 543 544 return sortedPosts; 545 } 546 547 /** 548 * Test if a post exists, provided its ID. 549 * @param id the post content ID. 550 * @param siteName the site name. 551 * @param language the language. 552 * @return the Post. 553 */ 554 public Post getPost(String siteName, String language, String id) 555 { 556 AllPostCache cache = getAllPostCache(siteName, language); 557 558 return cache.getPost(id); 559 } 560 561 /** 562 * Test if a post exists, provided its content name. 563 * @param name the post content name. 564 * @param siteName the site name. 565 * @param language the language. 566 * @return true if the post exists. 567 */ 568 public Post getPostByName(String siteName, String language, String name) 569 { 570 AllPostCache cache = getAllPostCache(siteName, language); 571 572 return cache.getPostByName(name); 573 } 574 575 576 /** 577 * Test if a post exists, provided its ID. 578 * @param id the post content ID. 579 * @param siteName the site name. 580 * @param language the language. 581 * @return true if the post exists. 582 */ 583 public boolean hasPost(String siteName, String language, String id) 584 { 585 AllPostCache cache = getAllPostCache(siteName, language); 586 587 return cache.hasPost(id); 588 } 589 590 /** 591 * Test if a post exists, provided its content name. 592 * @param name the post content name. 593 * @param siteName the site name. 594 * @param language the language. 595 * @return true if the post exists. 596 */ 597 public boolean hasPostByName(String siteName, String language, String name) 598 { 599 AllPostCache cache = getAllPostCache(siteName, language); 600 601 return cache.hasPostByName(name); 602 } 603 604 /** 605 * Add a post content to the cache. 606 * @param siteName The site name 607 * @param language The language 608 * @param postContent The content 609 */ 610 public void addPost(String siteName, String language, Content postContent) 611 { 612 PostDateCache dateCache = getPostDateCache(siteName, language); 613 PostTagCache tagCache = getPostTagCache(siteName, language); 614 AllPostCache allCache = getAllPostCache(siteName, language); 615 FuturePostCache futureCache = getFuturePostCache(siteName, language); 616 617 boolean displayFuturePosts = siteName != null ? BooleanUtils.isTrue(_siteConf.getValueAsBoolean(siteName, "display-future-posts")) : true; 618 619 boolean isFuturePost = _isFuturePost(postContent); 620 if (displayFuturePosts || !isFuturePost) 621 { 622 dispatchPost(dateCache, postContent); 623 dispatchPost(tagCache, postContent); 624 dispatchPost(allCache, postContent); 625 } 626 else // => !displayFuturePosts && isFuturePost 627 { 628 dispatchPost(futureCache, postContent); 629 } 630 } 631 632 /** 633 * Modify a post content in the cache. 634 * @param siteName The site name 635 * @param language The language 636 * @param postContent The content 637 */ 638 public void modifyPost(String siteName, String language, Content postContent) 639 { 640 PostDateCache dateCache = getPostDateCache(siteName, language); 641 PostTagCache tagCache = getPostTagCache(siteName, language); 642 AllPostCache allCache = getAllPostCache(siteName, language); 643 FuturePostCache futureCache = getFuturePostCache(siteName, language); 644 645 Post post = allCache.getPost(postContent.getId()); 646 Post futurePost = futureCache.getPost(postContent.getId()); 647 648 boolean displayFuturePosts = siteName != null ? BooleanUtils.isTrue(_siteConf.getValueAsBoolean(siteName, "display-future-posts")) : true; 649 boolean isFuturePost = _isFuturePost(postContent); 650 651 if (displayFuturePosts || !isFuturePost) 652 { 653 if (futurePost != null) 654 { 655 removePost(futureCache, futurePost); 656 } 657 658 if (post == null) 659 { 660 dispatchPost(dateCache, postContent); 661 dispatchPost(tagCache, postContent); 662 dispatchPost(allCache, postContent); 663 } 664 else 665 { 666 modifyPost(dateCache, post, postContent); 667 modifyPost(tagCache, post, postContent); 668 modifyPost(allCache, post, postContent); 669 } 670 } 671 else // => !displayFuturePosts && isFuturePost 672 { 673 if (futurePost == null) 674 { 675 dispatchPost(futureCache, postContent); 676 } 677 else 678 { 679 modifyPost(futureCache, futurePost, postContent); 680 } 681 682 if (post != null) 683 { 684 removePost(dateCache, post); 685 removePost(tagCache, post); 686 removePost(allCache, post); 687 } 688 } 689 } 690 691 /** 692 * Validate a post content. 693 * @param siteName The site name 694 * @param language The language 695 * @param postContent The content 696 */ 697 public void validatePost(String siteName, String language, Content postContent) 698 { 699 boolean displayFuturePosts = siteName != null ? BooleanUtils.isTrue(_siteConf.getValueAsBoolean(siteName, "display-future-posts")) : true; 700 701 // Get the "live" caches. 702 PostDateCache dateCache = getOrCreatePostDateCache(WebConstants.LIVE_WORKSPACE, siteName, language); 703 PostTagCache tagCache = getOrCreatePostTagCache(WebConstants.LIVE_WORKSPACE, siteName, language); 704 AllPostCache allCache = getOrCreateAllPostCache(WebConstants.LIVE_WORKSPACE, siteName, language); 705 FuturePostCache futureCache = getOrCreateFuturePostCache(WebConstants.LIVE_WORKSPACE, siteName, language); 706 707 if (displayFuturePosts || !_isFuturePost(postContent)) 708 { 709 // Dispatch the post in the live caches. 710 removePost(futureCache, postContent); 711 712 dispatchPost(dateCache, postContent); 713 dispatchPost(tagCache, postContent); 714 dispatchPost(allCache, postContent); 715 } 716 else // => !displayFuturePosts && isFuturePost 717 { 718 dispatchPost(futureCache, postContent); 719 720 removePost(dateCache, postContent); 721 removePost(tagCache, postContent); 722 removePost(allCache, postContent); 723 } 724 } 725 726 /** 727 * Remove a post content from the cache. 728 * @param siteName The site name 729 * @param language The language 730 * @param postContent The content 731 */ 732 public void removePost(String siteName, String language, Content postContent) 733 { 734 PostDateCache dateCache = getPostDateCache(siteName, language); 735 PostTagCache tagCache = getPostTagCache(siteName, language); 736 AllPostCache allCache = getAllPostCache(siteName, language); 737 FuturePostCache futureCache = getFuturePostCache(siteName, language); 738 739 removePost(dateCache, postContent); 740 removePost(tagCache, postContent); 741 removePost(allCache, postContent); 742 removePost(futureCache, postContent); 743 } 744 745 /** 746 * Remove a post content from the cache. 747 * @param siteName the site name. 748 * @param postId the post ID. 749 */ 750 public void removePostById(String siteName, String postId) 751 { 752 PostSiteLang postWithSiteLang = getPost(siteName, postId); 753 PostSiteLang futurePostWithSiteLang = getFuturePost(siteName, postId); 754 755 if (postWithSiteLang != null) 756 { 757 Post post = postWithSiteLang.getPost(); 758 String language = postWithSiteLang.getLanguage(); 759 760 // Get cache from current workspace 761 PostDateCache dateCache = getPostDateCache(siteName, language); 762 PostTagCache tagCache = getPostTagCache(siteName, language); 763 AllPostCache allCache = getAllPostCache(siteName, language); 764 765 // Get cache from live workspace 766 PostDateCache workspaceDateCache = getOrCreatePostDateCache(WebConstants.LIVE_WORKSPACE, siteName, language); 767 PostTagCache workspaceTagCache = getOrCreatePostTagCache(WebConstants.LIVE_WORKSPACE, siteName, language); 768 AllPostCache workspaceAllCache = getOrCreateAllPostCache(WebConstants.LIVE_WORKSPACE, siteName, language); 769 770 // Update the cache 771 removePost(dateCache, post); 772 removePost(tagCache, post); 773 removePost(allCache, post); 774 775 removePost(workspaceDateCache, post); 776 removePost(workspaceTagCache, post); 777 removePost(workspaceAllCache, post); 778 } 779 780 if (futurePostWithSiteLang != null) 781 { 782 Post post = futurePostWithSiteLang.getPost(); 783 String language = futurePostWithSiteLang.getLanguage(); 784 785 // Get cache from current workspace 786 FuturePostCache futureCache = getFuturePostCache(siteName, language); 787 788 // Get cache from live workspace 789 FuturePostCache workspaceFutureCache = getOrCreateFuturePostCache(WebConstants.LIVE_WORKSPACE, siteName, language); 790 791 // Update the cache 792 removePost(futureCache, post); 793 removePost(workspaceFutureCache, post); 794 } 795 } 796 797 /** 798 * Clear the caches. 799 */ 800 public void clearCaches() 801 { 802 _postsByDate.clear(); 803 _postsByTag.clear(); 804 _allPosts.clear(); 805 _futurePostsCache.clear(); 806 807 _initialized.clear(); 808 } 809 810 /** 811 * Clear the caches for a given siteName and language. 812 * @param siteName The site name 813 * @param language The language 814 */ 815 public void clearCaches(String siteName, String language) 816 { 817 for (String workspaceName : _postsByDate.keySet()) 818 { 819 PostDateCache dateCache = getOrCreatePostDateCache(workspaceName, siteName, language); 820 dateCache.clear(); 821 } 822 823 for (String workspaceName : _postsByTag.keySet()) 824 { 825 PostTagCache tagCache = getOrCreatePostTagCache(workspaceName, siteName, language); 826 tagCache.clear(); 827 } 828 829 for (String workspaceName : _allPosts.keySet()) 830 { 831 AllPostCache cache = getOrCreateAllPostCache(workspaceName, siteName, language); 832 cache.clear(); 833 } 834 835 for (String workspaceName : _futurePostsCache.keySet()) 836 { 837 FuturePostCache cache = getOrCreateFuturePostCache(workspaceName, siteName, language); 838 cache.clear(); 839 } 840 841 for (String workspaceName : _initialized.keySet()) 842 { 843 setInitialized(workspaceName, siteName, language, Boolean.FALSE); 844 } 845 } 846 847 /** 848 * Get the post date cache. 849 * @param siteName The site name 850 * @param language The language 851 * @return the post date cache for the given site and language. 852 */ 853 protected PostDateCache getPostDateCache(String siteName, String language) 854 { 855 // Get the workspace name. 856 String workspaceName = _workspaceSelector.getWorkspace(); 857 858 // Ensure the cache is created and initialized for the site and language. 859 if (!isInitialized(workspaceName, siteName, language)) 860 { 861 initializeCaches(workspaceName, siteName, language); 862 } 863 864 // Update the future posts cache. 865 _updateFuturePosts(workspaceName, siteName, language); 866 867 return _postsByDate.get(workspaceName).get(siteName).get(language); 868 } 869 870 /** 871 * Get the post date cache. 872 * @param workspaceName The workspace 873 * @param siteName The site name 874 * @param language The language 875 * @return the post date cache for the given site and language. 876 */ 877 protected PostDateCache getOrCreatePostDateCache(String workspaceName, String siteName, String language) 878 { 879 Map<String, Map<String, PostDateCache>> workspaceCache; 880 if (_postsByDate.containsKey(workspaceName)) 881 { 882 workspaceCache = _postsByDate.get(workspaceName); 883 } 884 else 885 { 886 workspaceCache = new HashMap<>(); 887 _postsByDate.put(workspaceName, workspaceCache); 888 } 889 890 Map<String, PostDateCache> siteCache; 891 if (workspaceCache.containsKey(siteName)) 892 { 893 siteCache = workspaceCache.get(siteName); 894 } 895 else 896 { 897 siteCache = new HashMap<>(); 898 workspaceCache.put(siteName, siteCache); 899 } 900 901 PostDateCache cache; 902 if (siteCache.containsKey(language)) 903 { 904 cache = siteCache.get(language); 905 } 906 else 907 { 908 cache = new PostDateCache(); 909 siteCache.put(language, cache); 910 } 911 912 return cache; 913 } 914 915 /** 916 * Get the post tag cache. 917 * @param siteName The site name 918 * @param language The language 919 * @return the post tag cache for the given site and language. 920 */ 921 protected PostTagCache getPostTagCache(String siteName, String language) 922 { 923 // Get the workspace name. 924 String workspaceName = _workspaceSelector.getWorkspace(); 925 926 // Ensure the cache is created and initialized for the site and language. 927 if (!isInitialized(workspaceName, siteName, language)) 928 { 929 initializeCaches(workspaceName, siteName, language); 930 } 931 932 // Update the future posts cache. 933 _updateFuturePosts(workspaceName, siteName, language); 934 935 return _postsByTag.get(workspaceName).get(siteName).get(language); 936 } 937 938 /** 939 * Get the post tag cache. 940 * @param workspaceName The workspace 941 * @param siteName The site name 942 * @param language The language 943 * @return the post tag cache for the given site and language. 944 */ 945 protected PostTagCache getOrCreatePostTagCache(String workspaceName, String siteName, String language) 946 { 947 Map<String, Map<String, PostTagCache>> workspaceCache; 948 if (_postsByTag.containsKey(workspaceName)) 949 { 950 workspaceCache = _postsByTag.get(workspaceName); 951 } 952 else 953 { 954 workspaceCache = new HashMap<>(); 955 _postsByTag.put(workspaceName, workspaceCache); 956 } 957 958 Map<String, PostTagCache> siteCache; 959 if (workspaceCache.containsKey(siteName)) 960 { 961 siteCache = workspaceCache.get(siteName); 962 } 963 else 964 { 965 siteCache = new HashMap<>(); 966 workspaceCache.put(siteName, siteCache); 967 } 968 969 PostTagCache cache; 970 if (siteCache.containsKey(language)) 971 { 972 cache = siteCache.get(language); 973 } 974 else 975 { 976 cache = new PostTagCache(); 977 siteCache.put(language, cache); 978 } 979 980 return cache; 981 } 982 983 /** 984 * Get the exhaustive post cache. 985 * @param siteName The site name 986 * @param language The language 987 * @return the post date cache for the given site and language. 988 */ 989 protected AllPostCache getAllPostCache(String siteName, String language) 990 { 991 // Get the workspace name. 992 String workspaceName = _workspaceSelector.getWorkspace(); 993 994 // Ensure the cache is created and initialized for the site and language. 995 if (!isInitialized(workspaceName, siteName, language)) 996 { 997 initializeCaches(workspaceName, siteName, language); 998 } 999 1000 // Update the future posts cache. 1001 _updateFuturePosts(workspaceName, siteName, language); 1002 1003 return _allPosts.get(workspaceName).get(siteName).get(language); 1004 } 1005 1006 /** 1007 * Get the exhaustive post cache. 1008 * @param workspaceName The workspace name 1009 * @param siteName The site name 1010 * @param language The language 1011 * @return the post date cache for the given site and language. 1012 */ 1013 protected AllPostCache getOrCreateAllPostCache(String workspaceName, String siteName, String language) 1014 { 1015 Map<String, Map<String, AllPostCache>> workspaceCache; 1016 if (_allPosts.containsKey(workspaceName)) 1017 { 1018 workspaceCache = _allPosts.get(workspaceName); 1019 } 1020 else 1021 { 1022 workspaceCache = new HashMap<>(); 1023 _allPosts.put(workspaceName, workspaceCache); 1024 } 1025 1026 Map<String, AllPostCache> siteCache; 1027 if (workspaceCache.containsKey(siteName)) 1028 { 1029 siteCache = workspaceCache.get(siteName); 1030 } 1031 else 1032 { 1033 siteCache = new HashMap<>(); 1034 workspaceCache.put(siteName, siteCache); 1035 } 1036 1037 AllPostCache cache; 1038 if (siteCache.containsKey(language)) 1039 { 1040 cache = siteCache.get(language); 1041 } 1042 else 1043 { 1044 cache = new AllPostCache(); 1045 siteCache.put(language, cache); 1046 } 1047 1048 return cache; 1049 } 1050 1051 1052 /** 1053 * Get the future post cache. 1054 * @param siteName the name of the site 1055 * @param language the language 1056 * @return the post date cache for the given site and language. 1057 */ 1058 protected FuturePostCache getFuturePostCache(String siteName, String language) 1059 { 1060 // Get the workspace name. 1061 String workspaceName = _workspaceSelector.getWorkspace(); 1062 1063 // Ensure the cache is created and initialized for the site and language. 1064 if (!isInitialized(workspaceName, siteName, language)) 1065 { 1066 initializeCaches(workspaceName, siteName, language); 1067 } 1068 1069 return _futurePostsCache.get(workspaceName).get(siteName).get(language); 1070 } 1071 1072 /** 1073 * Get the future post cache. 1074 * @param workspaceName the name of the workspace 1075 * @param siteName the name of the site 1076 * @param language the language 1077 * @return the future post cache for the given site and language. 1078 */ 1079 protected FuturePostCache getOrCreateFuturePostCache(String workspaceName, String siteName, String language) 1080 { 1081 Map<String, Map<String, FuturePostCache>> workspaceCache; 1082 if (_futurePostsCache.containsKey(workspaceName)) 1083 { 1084 workspaceCache = _futurePostsCache.get(workspaceName); 1085 } 1086 else 1087 { 1088 workspaceCache = new HashMap<>(); 1089 _futurePostsCache.put(workspaceName, workspaceCache); 1090 } 1091 1092 Map<String, FuturePostCache> siteCache; 1093 if (workspaceCache.containsKey(siteName)) 1094 { 1095 siteCache = workspaceCache.get(siteName); 1096 } 1097 else 1098 { 1099 siteCache = new HashMap<>(); 1100 workspaceCache.put(siteName, siteCache); 1101 } 1102 1103 FuturePostCache cache; 1104 if (siteCache.containsKey(language)) 1105 { 1106 cache = siteCache.get(language); 1107 } 1108 else 1109 { 1110 cache = new FuturePostCache(); 1111 siteCache.put(language, cache); 1112 } 1113 1114 return cache; 1115 } 1116 1117 /** 1118 * Get the exhaustive post cache. 1119 * @param workspaceName The workspace name 1120 * @param siteName The site name 1121 * @param language The language 1122 * @return the post date cache for the given site and language. 1123 */ 1124 protected boolean isInitialized(String workspaceName, String siteName, String language) 1125 { 1126 if (_initialized.containsKey(workspaceName)) 1127 { 1128 Map<String, Map<String, Boolean>> workspaceInitialized = _initialized.get(workspaceName); 1129 if (workspaceInitialized.containsKey(siteName)) 1130 { 1131 Map<String, Boolean> siteInitialized = workspaceInitialized.get(siteName); 1132 1133 if (siteInitialized.containsKey(language)) 1134 { 1135 return siteInitialized.get(language); 1136 } 1137 } 1138 } 1139 1140 return false; 1141 } 1142 1143 /** 1144 * Set the initialized map 1145 * @param siteName The site name 1146 * @param language The language 1147 */ 1148 protected void setInitialized(String siteName, String language) 1149 { 1150 setInitialized(siteName, language, Boolean.TRUE); 1151 } 1152 1153 /** 1154 * Set the initialized map 1155 * @param siteName The site name 1156 * @param language The language 1157 * @param initialized true to set initialized 1158 */ 1159 protected void setInitialized(String siteName, String language, Boolean initialized) 1160 { 1161 String workspaceName = _workspaceSelector.getWorkspace(); 1162 1163 setInitialized(workspaceName, siteName, language, initialized); 1164 } 1165 1166 /** 1167 * Set the initialized map 1168 * @param workspaceName The workspace name 1169 * @param siteName The site name 1170 * @param language The language 1171 * @param initialized true if to set initialized 1172 */ 1173 protected void setInitialized(String workspaceName, String siteName, String language, Boolean initialized) 1174 { 1175 Map<String, Map<String, Boolean>> workspaceInitialized; 1176 if (_initialized.containsKey(workspaceName)) 1177 { 1178 workspaceInitialized = _initialized.get(workspaceName); 1179 } 1180 else 1181 { 1182 workspaceInitialized = new HashMap<>(); 1183 _initialized.put(workspaceName, workspaceInitialized); 1184 } 1185 1186 Map<String, Boolean> siteInitialized; 1187 if (workspaceInitialized.containsKey(siteName)) 1188 { 1189 siteInitialized = workspaceInitialized.get(siteName); 1190 } 1191 else 1192 { 1193 siteInitialized = new HashMap<>(); 1194 workspaceInitialized.put(siteName, siteInitialized); 1195 } 1196 1197 siteInitialized.put(language, initialized); 1198 } 1199 1200 /** 1201 * Initialize all the caches. 1202 * @param workspaceName The workspace name 1203 * @param siteName The site name 1204 * @param language The language 1205 */ 1206 protected void initializeCaches(String workspaceName, String siteName, String language) 1207 { 1208 PostDateCache dateCache = getOrCreatePostDateCache(workspaceName, siteName, language); 1209 PostTagCache tagCache = getOrCreatePostTagCache(workspaceName, siteName, language); 1210 AllPostCache allCache = getOrCreateAllPostCache(workspaceName, siteName, language); 1211 FuturePostCache futureCache = getOrCreateFuturePostCache(workspaceName, siteName, language); 1212 1213 ContentTypeExpression cTypeExpr = new ContentTypeExpression(Operator.EQ, PostContentType.CONTENT_TYPE); 1214 Expression siteExpr = new StringExpression(DefaultWebContent.METADATA_SITE, Operator.EQ, siteName); 1215 Expression languageExpr = new ContentLanguageExpression(Operator.EQ, language); 1216 MetadataExpression dateExpr = new MetadataExpression("date"); 1217 1218 Expression expression = new AndExpression(cTypeExpr, siteExpr, languageExpr, dateExpr); 1219 1220 boolean displayFuturePosts = siteName != null ? BooleanUtils.isTrue(_siteConf.getValueAsBoolean(siteName, "display-future-posts")) : true; 1221 1222 SortCriteria sort = new SortCriteria(); 1223 sort.addCriterion("date", false, false); 1224 sort.addJCRPropertyCriterion("ametys:lastModified", false, false); 1225 1226 String query = QueryHelper.getXPathQuery(null, "ametys:content", expression, sort); 1227 1228 try (AmetysObjectIterable<Content> posts = _ametysResolver.query(query);) 1229 { 1230 for (Content postContent : posts) 1231 { 1232 if (displayFuturePosts || !_isFuturePost(postContent)) 1233 { 1234 dispatchPost(dateCache, postContent); 1235 dispatchPost(tagCache, postContent); 1236 dispatchPost(allCache, postContent); 1237 } 1238 else 1239 { 1240 dispatchPost(futureCache, postContent); 1241 } 1242 } 1243 setInitialized(siteName, language); 1244 } 1245 } 1246 1247 private void _updateFuturePosts(String workspaceName, String siteName, String language) 1248 { 1249 Date now = new Date(); 1250 1251 Calendar calendar = Calendar.getInstance(); 1252 calendar.setTime(now); 1253 1254 int minutes = calendar.get(Calendar.MINUTE); 1255 1256 int mod = minutes % 15; 1257 calendar.add(Calendar.MINUTE, -mod); 1258 calendar.set(Calendar.SECOND, 0); 1259 calendar.set(Calendar.MILLISECOND, 0); 1260 1261 Date roundedNow = calendar.getTime(); 1262 1263 if (_needUpdateFuturePosts(workspaceName, siteName, language, roundedNow)) 1264 { 1265 // Update the last update time 1266 _lastUpdateFuturePosts.get(workspaceName).get(siteName).put(language, roundedNow); 1267 1268 // Remove past "future posts" 1269 FuturePostCache futureCache = getOrCreateFuturePostCache(workspaceName, siteName, language); 1270 Collection<Post> pastPosts = futureCache.removePastPosts(); 1271 1272 // Notify that the posts are pasts, and must be visible now. 1273 for (Post pastPost : pastPosts) 1274 { 1275 if (_resolver.hasAmetysObjectForId(pastPost.getId())) 1276 { 1277 Content content = _resolver.resolveById(pastPost.getId()); 1278 1279 Map<String, Object> eventParams = new HashMap<>(); 1280 eventParams.put(ObservationConstants.ARGS_CONTENT, content); 1281 _observationManager.notify(new Event(BlogObservationConstants.EVENT_FUTURE_CONTENT_VISIBLE, new UserIdentity("admin", "admin"), eventParams)); 1282 } 1283 } 1284 } 1285 } 1286 1287 private boolean _needUpdateFuturePosts(String workspaceName, String siteName, String language, Date roundedNow) 1288 { 1289 Date lastUpdate = _getOrCreateLastUpdateFuturePosts(workspaceName, siteName, language); 1290 1291 if (lastUpdate != null) 1292 { 1293 long duration = roundedNow.getTime() - lastUpdate.getTime(); 1294 long diffInMinutes = TimeUnit.MILLISECONDS.toMinutes(duration); 1295 1296 return diffInMinutes >= 15; 1297 } 1298 1299 return true; 1300 } 1301 1302 private Date _getOrCreateLastUpdateFuturePosts(String workspaceName, String siteName, String language) 1303 { 1304 Map<String, Map<String, Date>> workspaceMap; 1305 if (_lastUpdateFuturePosts.containsKey(workspaceName)) 1306 { 1307 workspaceMap = _lastUpdateFuturePosts.get(workspaceName); 1308 } 1309 else 1310 { 1311 workspaceMap = new HashMap<>(); 1312 _lastUpdateFuturePosts.put(workspaceName, workspaceMap); 1313 } 1314 1315 Map<String, Date> siteMap; 1316 if (workspaceMap.containsKey(siteName)) 1317 { 1318 siteMap = workspaceMap.get(siteName); 1319 } 1320 else 1321 { 1322 siteMap = new HashMap<>(); 1323 workspaceMap.put(siteName, siteMap); 1324 } 1325 1326 Date lastUpdate = null; 1327 if (siteMap.containsKey(language)) 1328 { 1329 lastUpdate = siteMap.get(language); 1330 } 1331 1332 return lastUpdate; 1333 } 1334 1335 private boolean _isFuturePost (Content post) 1336 { 1337 Date postDate = post.getMetadataHolder().getDate(POST_METADATA_DATE, null); 1338 return postDate != null && postDate.after(new Date()); 1339 } 1340 1341 /** 1342 * Get a post by its site name and id. 1343 * @param siteName the site name. 1344 * @param postId the post ID. 1345 * @return the Post if it exists in the cache, or null if it doesn't. 1346 */ 1347 protected PostSiteLang getPost(String siteName, String postId) 1348 { 1349 String workspaceName = _workspaceSelector.getWorkspace(); 1350 if (_allPosts.containsKey(workspaceName)) 1351 { 1352 Map<String, Map<String, AllPostCache>> workspacePosts = _allPosts.get(workspaceName); 1353 1354 if (workspacePosts.containsKey(siteName)) 1355 { 1356 Map<String, AllPostCache> sitePosts = workspacePosts.get(siteName); 1357 1358 for (Entry<String, AllPostCache> langPosts : sitePosts.entrySet()) 1359 { 1360 if (langPosts.getValue().hasPost(postId)) 1361 { 1362 String language = langPosts.getKey(); 1363 Post post = langPosts.getValue().getPost(postId); 1364 1365 return new PostSiteLang(post, siteName, language); 1366 } 1367 } 1368 } 1369 } 1370 1371 return null; 1372 } 1373 1374 /** 1375 * Get a future post by its site name and id. 1376 * @param siteName the site name. 1377 * @param postId the post ID. 1378 * @return the Post if it exists in the cache, or null if it doesn't. 1379 */ 1380 protected PostSiteLang getFuturePost(String siteName, String postId) 1381 { 1382 String workspaceName = _workspaceSelector.getWorkspace(); 1383 if (_futurePostsCache.containsKey(workspaceName)) 1384 { 1385 Map<String, Map<String, FuturePostCache>> workspacePosts = _futurePostsCache.get(workspaceName); 1386 1387 if (workspacePosts.containsKey(siteName)) 1388 { 1389 Map<String, FuturePostCache> sitePosts = workspacePosts.get(siteName); 1390 1391 for (Entry<String, FuturePostCache> langPosts : sitePosts.entrySet()) 1392 { 1393 if (langPosts.getValue().hasPost(postId)) 1394 { 1395 String language = langPosts.getKey(); 1396 Post post = langPosts.getValue().getPost(postId); 1397 1398 return new PostSiteLang(post, siteName, language); 1399 } 1400 } 1401 } 1402 } 1403 1404 return null; 1405 } 1406 1407 /** 1408 * Dispatch a post content in the post date cache. 1409 * @param dateCache the post date cache. 1410 * @param content the Post content. 1411 */ 1412 protected void dispatchPost(PostDateCache dateCache, Content content) 1413 { 1414 CompositeMetadata meta = content.getMetadataHolder(); 1415 Date postDate = meta.getDate("date", null); 1416 1417 // Dispatch by date. 1418 if (postDate != null) 1419 { 1420 DateTime dateTime = new DateTime(postDate.getTime()); 1421 int month = dateTime.getMonthOfYear(); 1422 int year = dateTime.getYear(); 1423 1424 dateCache.addPost(content, year, month); 1425 } 1426 } 1427 1428 /** 1429 * Add a post in the post date cache. 1430 * @param dateCache the post date cache. 1431 * @param post the Post to add. 1432 */ 1433 protected void dispatchPost(PostDateCache dateCache, Post post) 1434 { 1435 Date postDate = post.getDate(); 1436 1437 // Dispatch by date. 1438 if (postDate != null) 1439 { 1440 DateTime dateTime = new DateTime(postDate.getTime()); 1441 int month = dateTime.getMonthOfYear(); 1442 int year = dateTime.getYear(); 1443 1444 dateCache.addPost(post, year, month); 1445 } 1446 } 1447 1448 /** 1449 * Dispatch a post content in the post tag cache. 1450 * @param tagCache the post tag cache. 1451 * @param content the Post content. 1452 */ 1453 protected void dispatchPost(PostTagCache tagCache, Content content) 1454 { 1455 CompositeMetadata meta = content.getMetadataHolder(); 1456 String[] postTags = meta.getStringArray("tags", new String[0]); 1457 1458 for (String tagName : postTags) 1459 { 1460 tagCache.addPost(content, tagName); 1461 } 1462 } 1463 1464 /** 1465 * Dispatch a post in the post tag cache. 1466 * @param tagCache the post tag cache. 1467 * @param post the Post to add. 1468 */ 1469 protected void dispatchPost(PostTagCache tagCache, Post post) 1470 { 1471 String[] postTags = post.getTags(); 1472 1473 for (String tagName : postTags) 1474 { 1475 tagCache.addPost(post, tagName); 1476 } 1477 } 1478 1479 /** 1480 * Dispatch a post content in the post cache. 1481 * @param cache the post cache. 1482 * @param content the Post content. 1483 */ 1484 protected void dispatchPost(AllPostCache cache, Content content) 1485 { 1486 cache.addPost(content); 1487 } 1488 1489 /** 1490 * Dispatch a post in the post cache. 1491 * @param cache the post cache. 1492 * @param post the Post content. 1493 */ 1494 protected void dispatchPost(AllPostCache cache, Post post) 1495 { 1496 cache.addPost(post); 1497 } 1498 1499 /** 1500 * Dispatch a post content in the future post cache. 1501 * @param cache the future post cache. 1502 * @param content the Post content. 1503 */ 1504 protected void dispatchPost(FuturePostCache cache, Content content) 1505 { 1506 cache.addPost(content); 1507 } 1508 1509 /** 1510 * Remove a post content. 1511 * @param dateCache the post date cache. 1512 * @param content the Post content. 1513 */ 1514 protected void removePost(PostDateCache dateCache, Content content) 1515 { 1516 CompositeMetadata meta = content.getMetadataHolder(); 1517 Date postDate = meta.getDate("date", null); 1518 1519 // Dispatch by date. 1520 if (postDate != null) 1521 { 1522 DateTime dateTime = new DateTime(postDate.getTime()); 1523 int month = dateTime.getMonthOfYear(); 1524 int year = dateTime.getYear(); 1525 1526 dateCache.removePost(content.getId(), year, month); 1527 } 1528 } 1529 1530 /** 1531 * Remove a post content. 1532 * @param dateCache the post date cache. 1533 * @param post the Post object to remove. 1534 */ 1535 protected void removePost(PostDateCache dateCache, Post post) 1536 { 1537 Date postDate = post.getDate(); 1538 1539 // Dispatch by date. 1540 if (postDate != null) 1541 { 1542 DateTime dateTime = new DateTime(postDate.getTime()); 1543 int month = dateTime.getMonthOfYear(); 1544 int year = dateTime.getYear(); 1545 1546 dateCache.removePost(post.getId(), year, month); 1547 } 1548 } 1549 1550 /** 1551 * Remove a post content. 1552 * @param cache the post tag cache. 1553 * @param content the Post content. 1554 */ 1555 protected void removePost(PostTagCache cache, Content content) 1556 { 1557 CompositeMetadata meta = content.getMetadataHolder(); 1558 String[] postTags = meta.getStringArray("tags", new String[0]); 1559 1560 for (String tagName : postTags) 1561 { 1562 cache.removePost(content.getId(), tagName); 1563 } 1564 } 1565 1566 /** 1567 * Remove a post content. 1568 * @param cache the post tag cache. 1569 * @param post the Post to remove. 1570 */ 1571 protected void removePost(PostTagCache cache, Post post) 1572 { 1573 String[] postTags = post.getTags(); 1574 1575 for (String tagName : postTags) 1576 { 1577 cache.removePost(post.getId(), tagName); 1578 } 1579 } 1580 1581 /** 1582 * Remove a post content. 1583 * @param cache the post cache. 1584 * @param content the Post content. 1585 */ 1586 protected void removePost(AllPostCache cache, Content content) 1587 { 1588 cache.removePost(content); 1589 } 1590 1591 /** 1592 * Remove a post content. 1593 * @param cache the post cache. 1594 * @param post the Post to remove. 1595 */ 1596 protected void removePost(AllPostCache cache, Post post) 1597 { 1598 cache.removePost(post.getId(), post.getName()); 1599 } 1600 1601 /** 1602 * Remove a future post content. 1603 * @param cache the future post cache. 1604 * @param content the Post content to remove. 1605 */ 1606 protected void removePost(FuturePostCache cache, Content content) 1607 { 1608 cache.removePost(content); 1609 } 1610 1611 /** 1612 * Remove a future post content. 1613 * @param cache the future post cache. 1614 * @param post the Post to remove. 1615 */ 1616 protected void removePost(FuturePostCache cache, Post post) 1617 { 1618 cache.removePost(post); 1619 } 1620 1621 /** 1622 * Remove a post content. 1623 * @param dateCache the post date cache. 1624 * @param post the post content 1625 * @param postContent the post Content. 1626 */ 1627 protected void modifyPost(PostDateCache dateCache, Post post, Content postContent) 1628 { 1629 Date oldDate = post.getDate(); 1630 Date newDate = postContent.getMetadataHolder().getDate("date"); 1631 1632 if (!newDate.equals(oldDate)) 1633 { 1634 if (oldDate != null) 1635 { 1636 DateTime oldDateTime = new DateTime(oldDate.getTime()); 1637 dateCache.removePost(postContent.getId(), oldDateTime.getYear(), oldDateTime.getMonthOfYear()); 1638 } 1639 1640 DateTime newDateTime = new DateTime(newDate.getTime()); 1641 dateCache.addPost(postContent, newDateTime.getYear(), newDateTime.getMonthOfYear()); 1642 } 1643 } 1644 1645 /** 1646 * Remove a post content. 1647 * @param cache the post tag cache. 1648 * @param post the post content 1649 * @param postContent the post Content. 1650 */ 1651 protected void modifyPost(PostTagCache cache, Post post, Content postContent) 1652 { 1653 CompositeMetadata meta = postContent.getMetadataHolder(); 1654 String[] oldPostTags = post.getTags(); 1655 String[] newPostTags = meta.getStringArray("tags", new String[0]); 1656 1657 for (String oldTagName : oldPostTags) 1658 { 1659 cache.removePost(postContent.getId(), oldTagName); 1660 } 1661 1662 for (String newTagName : newPostTags) 1663 { 1664 cache.addPost(postContent, newTagName); 1665 } 1666 } 1667 1668 /** 1669 * Modify a post content. 1670 * @param cache the post cache. 1671 * @param post the post content 1672 * @param postContent the post Content. 1673 */ 1674 protected void modifyPost(AllPostCache cache, Post post, Content postContent) 1675 { 1676 cache.removePost(postContent); 1677 cache.addPost(postContent); 1678 } 1679 1680 /** 1681 * Modify a future post content. 1682 * @param cache the post cache. 1683 * @param post the post 1684 * @param postContent the post Content. 1685 */ 1686 protected void modifyPost(FuturePostCache cache, Post post, Content postContent) 1687 { 1688 cache.removePost(postContent); 1689 cache.addPost(postContent); 1690 } 1691 1692 /** 1693 * Class representing a Post in the cache. 1694 */ 1695 public class Post 1696 { 1697 1698 /** The post content id. */ 1699 protected String _id; 1700 1701 /** The post content name. */ 1702 protected String _name; 1703 1704 /** The post date. */ 1705 protected Date _date; 1706 1707 /** The post tags. */ 1708 protected String[] _tags; 1709 1710 /** 1711 * Get the id. 1712 * @return the id 1713 */ 1714 public String getId() 1715 { 1716 return _id; 1717 } 1718 1719 /** 1720 * Set the id. 1721 * @param id the id to set 1722 */ 1723 public void setId(String id) 1724 { 1725 this._id = id; 1726 } 1727 1728 /** 1729 * Get the name. 1730 * @return the name 1731 */ 1732 public String getName() 1733 { 1734 return _name; 1735 } 1736 1737 /** 1738 * Set the name. 1739 * @param name the name to set 1740 */ 1741 public void setName(String name) 1742 { 1743 this._name = name; 1744 } 1745 1746 /** 1747 * Get the tags. 1748 * @return the tags 1749 */ 1750 public String[] getTags() 1751 { 1752 return _tags; 1753 } 1754 1755 /** 1756 * Set the tags. 1757 * @param tags the tags to set 1758 */ 1759 public void setTags(String[] tags) 1760 { 1761 this._tags = new String[tags.length]; 1762 System.arraycopy(tags, 0, _tags, 0, tags.length); 1763 } 1764 1765 /** 1766 * Get the date. 1767 * @return the date 1768 */ 1769 public Date getDate() 1770 { 1771 return _date; 1772 } 1773 1774 /** 1775 * Set the date. 1776 * @param date the date to set 1777 */ 1778 public void setDate(Date date) 1779 { 1780 this._date = date; 1781 } 1782 1783 @Override 1784 public int hashCode() 1785 { 1786 return _id.hashCode(); 1787 } 1788 1789 @Override 1790 public boolean equals(Object obj) 1791 { 1792 if (this == obj) 1793 { 1794 return true; 1795 } 1796 if (obj == null) 1797 { 1798 return false; 1799 } 1800 if (getClass() != obj.getClass()) 1801 { 1802 return false; 1803 } 1804 Post other = (Post) obj; 1805 if (_id == null) 1806 { 1807 if (other._id != null) 1808 { 1809 return false; 1810 } 1811 } 1812 else if (!_id.equals(other._id)) 1813 { 1814 return false; 1815 } 1816 return true; 1817 } 1818 1819 } 1820 1821 /** 1822 * A Post with site name and language. 1823 */ 1824 protected class PostSiteLang extends Post 1825 { 1826 1827 /** The post. */ 1828 protected Post _post; 1829 1830 /** The post site name. */ 1831 protected String _siteName; 1832 1833 /** The post language. */ 1834 protected String _language; 1835 1836 /** 1837 * Default constructor. 1838 */ 1839 public PostSiteLang() 1840 { 1841 // Nothing to do. 1842 } 1843 1844 /** 1845 * Constructor with all parameters. 1846 * @param post the Post. 1847 * @param siteName the site name. 1848 * @param language the language. 1849 */ 1850 public PostSiteLang(Post post, String siteName, String language) 1851 { 1852 super(); 1853 this._post = post; 1854 this._siteName = siteName; 1855 this._language = language; 1856 } 1857 1858 /** 1859 * Get the post. 1860 * @return the post 1861 */ 1862 public Post getPost() 1863 { 1864 return _post; 1865 } 1866 1867 /** 1868 * Set the post. 1869 * @param post the post to set 1870 */ 1871 public void setPost(Post post) 1872 { 1873 this._post = post; 1874 } 1875 1876 /** 1877 * Get the siteName. 1878 * @return the siteName 1879 */ 1880 public String getSiteName() 1881 { 1882 return _siteName; 1883 } 1884 1885 /** 1886 * Set the siteName. 1887 * @param siteName the siteName to set 1888 */ 1889 public void setSiteName(String siteName) 1890 { 1891 this._siteName = siteName; 1892 } 1893 1894 /** 1895 * Get the language. 1896 * @return the language 1897 */ 1898 public String getLanguage() 1899 { 1900 return _language; 1901 } 1902 1903 /** 1904 * Set the language. 1905 * @param language the language to set 1906 */ 1907 public void setLanguage(String language) 1908 { 1909 this._language = language; 1910 } 1911 1912 } 1913 1914 /** 1915 * Post cache by date. 1916 */ 1917 protected class PostDateCache 1918 { 1919 1920 /** The posts, indexed by year, month and ID. */ 1921 private Map<Integer, Map<Integer, Map<String, Post>>> _posts; 1922 1923 /** 1924 * Construct a PostDateCache instance. 1925 */ 1926 public PostDateCache() 1927 { 1928 _posts = new HashMap<>(); 1929 } 1930 1931 /** 1932 * Determines if the year page exists 1933 * @param year the year 1934 * @return true if there are posts for this year 1935 */ 1936 public boolean hasYear(int year) 1937 { 1938 return _posts.containsKey(year); 1939 } 1940 1941 /** 1942 * Get the years with posts. 1943 * @return the years with posts as a Set of Integer. 1944 */ 1945 public Set<Integer> getYears() 1946 { 1947 return _posts.keySet(); 1948 } 1949 1950 /** 1951 * Determines if the page month exists in given year 1952 * @param year The year 1953 * @param month The month 1954 * @return true if there are posts for this year/month couple 1955 */ 1956 public boolean hasMonth(int year, int month) 1957 { 1958 boolean hasMonth = false; 1959 1960 if (_posts.containsKey(year)) 1961 { 1962 hasMonth = _posts.get(year).containsKey(month); 1963 } 1964 1965 return hasMonth; 1966 } 1967 1968 /** 1969 * Get the months with posts for a specified year. 1970 * @param year the year. 1971 * @return the months with posts in the specified year as a Set of Integer. 1972 */ 1973 public Set<Integer> getMonths(int year) 1974 { 1975 Set<Integer> months = new HashSet<>(); 1976 1977 if (_posts.containsKey(year)) 1978 { 1979 months = _posts.get(year).keySet(); 1980 } 1981 1982 return months; 1983 } 1984 1985 /** 1986 * Get the posts for a year couple. 1987 * @param year the year. 1988 * @return the posts in the specified year. 1989 */ 1990 public Map<String, Post> getPosts(int year) 1991 { 1992 Map<String, Post> posts = new HashMap<>(); 1993 1994 if (_posts.containsKey(year)) 1995 { 1996 Map<Integer, Map<String, Post>> months = _posts.get(year); 1997 1998 for (Map<String, Post> month : months.values()) 1999 { 2000 posts.putAll(month); 2001 } 2002 } 2003 2004 return posts; 2005 } 2006 2007 /** 2008 * Get the posts for a year-month couple. 2009 * @param year the year. 2010 * @param month the month. 2011 * @return the posts in the specified year and month. 2012 */ 2013 public Map<String, Post> getPosts(int year, int month) 2014 { 2015 Map<String, Post> posts = new HashMap<>(); 2016 2017 if (_posts.containsKey(year)) 2018 { 2019 Map<Integer, Map<String, Post>> months = _posts.get(year); 2020 2021 if (months.containsKey(month)) 2022 { 2023 posts = months.get(month); 2024 } 2025 } 2026 2027 return posts; 2028 } 2029 2030 /** 2031 * Add a post to the cache. 2032 * @param content The content 2033 * @param year the year 2034 * @param month the month 2035 */ 2036 public void addPost(Content content, int year, int month) 2037 { 2038 Map<String, Post> posts = getOrCreatePostMap(year, month); 2039 2040 CompositeMetadata meta = content.getMetadataHolder(); 2041 2042 Post post = new Post(); 2043 post.setId(content.getId()); 2044 post.setName(content.getName()); 2045 post.setDate(meta.getDate("date")); 2046 post.setTags(meta.getStringArray("tags", new String[0])); 2047 2048 posts.put(post.getId(), post); 2049 } 2050 2051 /** 2052 * Add a post to the cache. 2053 * @param post the post 2054 * @param year the year of the post 2055 * @param month the month of the post 2056 */ 2057 public void addPost(Post post, int year, int month) 2058 { 2059 Map<String, Post> posts = getOrCreatePostMap(year, month); 2060 posts.put(post.getId(), post); 2061 } 2062 2063 /** 2064 * Remove a post from the cache. 2065 * @param contentId The content id 2066 * @param year the year 2067 * @param month the month 2068 */ 2069 public void removePost(String contentId, int year, int month) 2070 { 2071 if (_posts.containsKey(year)) 2072 { 2073 Map<Integer, Map<String, Post>> months = _posts.get(year); 2074 2075 if (months.containsKey(month)) 2076 { 2077 Map<String, Post> posts = months.get(month); 2078 2079 posts.remove(contentId); 2080 if (posts.isEmpty()) 2081 { 2082 months.remove(month); 2083 } 2084 } 2085 2086 if (months.isEmpty()) 2087 { 2088 _posts.remove(year); 2089 } 2090 } 2091 } 2092 2093 /** 2094 * Test if the post with the given ID exists for this year and month. 2095 * @param year the year. 2096 * @param month the month. 2097 * @param id the id of post 2098 * @return the posts in the specified year and month. 2099 */ 2100 public boolean hasPost(int year, int month, String id) 2101 { 2102 return getPosts(year, month).containsKey(id); 2103 } 2104 2105 /** 2106 * Get the posts for a year-month couple. 2107 * @param year the year. 2108 * @param month the month. 2109 * @param name the name of post 2110 * @return the posts in the specified year and month. 2111 */ 2112 public Post getPostByName(int year, int month, String name) 2113 { 2114 Post post = null; 2115 2116 Iterator<Post> posts = getPosts(year, month).values().iterator(); 2117 while (posts.hasNext() && post == null) 2118 { 2119 Post currentPost = posts.next(); 2120 if (currentPost.getName().equals(name)) 2121 { 2122 post = currentPost; 2123 } 2124 } 2125 2126 return post; 2127 } 2128 2129 /** 2130 * Get the posts for a year-month couple. 2131 * @param year the year. 2132 * @param month the month. 2133 * @param name the name of post 2134 * @return the posts in the specified year and month. 2135 */ 2136 public boolean hasPostByName(int year, int month, String name) 2137 { 2138 boolean hasPost = false; 2139 2140 Iterator<Post> posts = getPosts(year, month).values().iterator(); 2141 while (posts.hasNext() && !hasPost) 2142 { 2143 hasPost = posts.next().getName().equals(name); 2144 } 2145 2146 return hasPost; 2147 } 2148 2149 /** 2150 * Clear the cache. 2151 */ 2152 public void clear() 2153 { 2154 _posts.clear(); 2155 } 2156 2157 /** 2158 * Get or create the post set for the specified year and month. 2159 * @param year the year. 2160 * @param month the month. 2161 * @return the post set. 2162 */ 2163 protected Map<String, Post> getOrCreatePostMap(int year, int month) 2164 { 2165 Map<String, Post> posts; 2166 2167 Map<Integer, Map<String, Post>> months; 2168 2169 if (_posts.containsKey(year)) 2170 { 2171 months = _posts.get(year); 2172 } 2173 else 2174 { 2175 months = new HashMap<>(); 2176 _posts.put(year, months); 2177 } 2178 2179 if (months.containsKey(month)) 2180 { 2181 posts = months.get(month); 2182 } 2183 else 2184 { 2185 posts = new LinkedHashMap<>(); 2186 months.put(month, posts); 2187 } 2188 2189 return posts; 2190 } 2191 2192 } 2193 2194 /** 2195 * Post cache by tag. 2196 */ 2197 protected class PostTagCache 2198 { 2199 2200 /** The posts, indexed by tag and ID. */ 2201 private Map<String, Map<String, Post>> _posts; 2202 2203 /** 2204 * Construct a PostTagCache instance. 2205 */ 2206 public PostTagCache() 2207 { 2208 _posts = new HashMap<>(); 2209 } 2210 2211 /** 2212 * Get the years with posts. 2213 * @param tagId The id of tag 2214 * @return the years with posts as a Set of Integer. 2215 */ 2216 public boolean hasTag(String tagId) 2217 { 2218 return _posts.containsKey(tagId); 2219 } 2220 2221 /** 2222 * Get the years with posts. 2223 * @return the years with posts as a Set of Integer. 2224 */ 2225 public Set<String> getTags() 2226 { 2227 return _posts.keySet(); 2228 } 2229 2230 /** 2231 * Get the posts for a year-month couple. 2232 * @param tagId the year. 2233 * @return the posts in the specified year and month. 2234 */ 2235 public Map<String, Post> getPosts(String tagId) 2236 { 2237 Map<String, Post> posts = new HashMap<>(); 2238 2239 if (_posts.containsKey(tagId)) 2240 { 2241 posts = _posts.get(tagId); 2242 } 2243 2244 return posts; 2245 } 2246 2247 /** 2248 * Add a post to the cache. 2249 * @param content The content 2250 * @param tagId the tag ID. 2251 */ 2252 public void addPost(Content content, String tagId) 2253 { 2254 Map<String, Post> posts = getOrCreatePostMap(tagId); 2255 2256 CompositeMetadata meta = content.getMetadataHolder(); 2257 2258 Post post = new Post(); 2259 post.setId(content.getId()); 2260 post.setName(content.getName()); 2261 post.setDate(meta.getDate("date")); 2262 post.setTags(meta.getStringArray("tags", new String[0])); 2263 2264 posts.put(post.getId(), post); 2265 } 2266 2267 /** 2268 * Add a post to the cache. 2269 * @param post the post 2270 * @param tagId the tag ID. 2271 */ 2272 public void addPost(Post post, String tagId) 2273 { 2274 Map<String, Post> posts = getOrCreatePostMap(tagId); 2275 posts.put(post.getId(), post); 2276 } 2277 2278 /** 2279 * Remove a post from the cache. 2280 * @param contentId The id of content 2281 * @param tagId the id of tag 2282 */ 2283 public void removePost(String contentId, String tagId) 2284 { 2285 if (_posts.containsKey(tagId)) 2286 { 2287 Map<String, Post> posts = _posts.get(tagId); 2288 2289 posts.remove(contentId); 2290 if (posts.isEmpty()) 2291 { 2292 _posts.remove(tagId); 2293 } 2294 } 2295 } 2296 2297 /** 2298 * Test if the post with the given ID exists for this year and month. 2299 * @param tagId the tag id. 2300 * @param id the post ID. 2301 * @return the posts in the specified year and month. 2302 */ 2303 public Post getPost(String tagId, String id) 2304 { 2305 return getPosts(tagId).get(id); 2306 } 2307 2308 /** 2309 * Test if the post with the given ID exists for this year and month. 2310 * @param tagId the tag id. 2311 * @param id the post ID. 2312 * @return the posts in the specified year and month. 2313 */ 2314 public boolean hasPost(String tagId, String id) 2315 { 2316 return getPosts(tagId).containsKey(id); 2317 } 2318 2319 /** 2320 * Get the posts for a year-month couple. 2321 * @param tagId the year. 2322 * @param name The name of post 2323 * @return the posts in the specified year and month. 2324 */ 2325 public Post getPostByName(String tagId, String name) 2326 { 2327 Post post = null; 2328 2329 Iterator<Post> posts = getPosts(tagId).values().iterator(); 2330 while (posts.hasNext() && post == null) 2331 { 2332 Post currentPost = posts.next(); 2333 if (currentPost.getName().equals(name)) 2334 { 2335 post = currentPost; 2336 } 2337 } 2338 2339 return post; 2340 } 2341 2342 /** 2343 * Get the posts for a year-month couple. 2344 * @param tagId the year. 2345 * @param name the name of post 2346 * @return the posts in the specified year and month. 2347 */ 2348 public boolean hasPostByName(String tagId, String name) 2349 { 2350 boolean hasPost = false; 2351 2352 Iterator<Post> posts = getPosts(tagId).values().iterator(); 2353 while (posts.hasNext() && !hasPost) 2354 { 2355 hasPost = posts.next().getName().equals(name); 2356 } 2357 2358 return hasPost; 2359 } 2360 2361 /** 2362 * Clear the cache. 2363 */ 2364 public void clear() 2365 { 2366 _posts.clear(); 2367 } 2368 2369 /** 2370 * Get or create the post set for the specified year and month. 2371 * @param tagId the year. 2372 * @return the post set, indexed by id. 2373 */ 2374 protected Map<String, Post> getOrCreatePostMap(String tagId) 2375 { 2376 Map<String, Post> posts; 2377 2378 if (_posts.containsKey(tagId)) 2379 { 2380 posts = _posts.get(tagId); 2381 } 2382 else 2383 { 2384 posts = new LinkedHashMap<>(); 2385 _posts.put(tagId, posts); 2386 } 2387 2388 return posts; 2389 } 2390 2391 } 2392 2393 /** 2394 * Cache of all the posts. 2395 */ 2396 protected class AllPostCache 2397 { 2398 2399 /** The posts, indexed by ID. */ 2400 private Map<String, Post> _posts; 2401 2402 /** The posts, indexed by name. */ 2403 private Map<String, Post> _postsByName; 2404 2405 /** 2406 * Construct a AllPostCache instance. 2407 */ 2408 public AllPostCache() 2409 { 2410 _posts = new LinkedHashMap<>(); 2411 _postsByName = new LinkedHashMap<>(); 2412 } 2413 2414 /** 2415 * Get a list of all the posts' IDs. 2416 * @return all the posts' IDs as a Set. 2417 */ 2418 public Set<String> getPostIds() 2419 { 2420 return Collections.unmodifiableSet(_posts.keySet()); 2421 } 2422 2423 /** 2424 * Get the exhaustive list of Posts. 2425 * @return the exhaustive list of Posts. 2426 */ 2427 public Map<String, Post> getPosts() 2428 { 2429 return Collections.unmodifiableMap(_posts); 2430 } 2431 2432 /** 2433 * Test if a post exists, provided its ID. 2434 * @param id the post content ID. 2435 * @return true if the post exists. 2436 */ 2437 public Post getPost(String id) 2438 { 2439 return _posts.get(id); 2440 } 2441 2442 /** 2443 * Test if a post exists, provided its ID. 2444 * @param id the post content ID. 2445 * @return true if the post exists. 2446 */ 2447 public boolean hasPost(String id) 2448 { 2449 return _posts.containsKey(id); 2450 } 2451 2452 /** 2453 * Test if a post exists, provided its content name. 2454 * @param name the post content name. 2455 * @return true if the post exists. 2456 */ 2457 public Post getPostByName(String name) 2458 { 2459 return _postsByName.get(name); 2460 } 2461 2462 /** 2463 * Test if a post exists, provided its content name. 2464 * @param name the post content name. 2465 * @return true if the post exists. 2466 */ 2467 public boolean hasPostByName(String name) 2468 { 2469 return _postsByName.containsKey(name); 2470 } 2471 2472 /** 2473 * Add a post to the cache. 2474 * @param content the post content. 2475 */ 2476 public void addPost(Content content) 2477 { 2478 CompositeMetadata meta = content.getMetadataHolder(); 2479 2480 Post post = new Post(); 2481 post.setId(content.getId()); 2482 post.setName(content.getName()); 2483 if (meta.hasMetadata("date")) 2484 { 2485 post.setDate(meta.getDate("date")); 2486 } 2487 post.setTags(meta.getStringArray("tags", new String[0])); 2488 2489 _posts.put(post.getId(), post); 2490 _postsByName.put(post.getName(), post); 2491 } 2492 2493 /** 2494 * Add a post to the cache. 2495 * @param post The post 2496 */ 2497 public void addPost(Post post) 2498 { 2499 _posts.put(post.getId(), post); 2500 _postsByName.put(post.getName(), post); 2501 } 2502 2503 /** 2504 * Remove a post from the cache. 2505 * @param content the post content to remove from the cache. 2506 */ 2507 public void removePost(Content content) 2508 { 2509 removePost(content.getId(), content.getName()); 2510 } 2511 2512 /** 2513 * Remove a post from the cache. 2514 * @param id The id of post 2515 * @param name The name of post 2516 */ 2517 public void removePost(String id, String name) 2518 { 2519 _posts.remove(id); 2520 _postsByName.remove(name); 2521 } 2522 2523 /** 2524 * Clear the cache. 2525 */ 2526 public void clear() 2527 { 2528 _posts.clear(); 2529 _postsByName.clear(); 2530 } 2531 } 2532 2533 /** 2534 * Cache of identifier of the future posts. 2535 */ 2536 protected class FuturePostCache 2537 { 2538 /** The posts, indexed by ID. */ 2539 private Map<String, Post> _posts; 2540 2541 /** The posts id, indexed by date. */ 2542 private Multimap<Date, String> _sortedPostIds; 2543 2544 /** 2545 * Construct a AllPostCache instance. 2546 */ 2547 public FuturePostCache() 2548 { 2549 _posts = new HashMap<>(); 2550 _sortedPostIds = TreeMultimap.create(); 2551 } 2552 2553 /** 2554 * Remove the post that are not future anymore (given the current date) 2555 * @return The collection of past posts 2556 */ 2557 public Collection<Post> removePastPosts() 2558 { 2559 Set<Post> pastPosts = new HashSet<>(); 2560 2561 Set<Date> sortedDates = _sortedPostIds.keySet(); 2562 Iterator<Date> iterator = sortedDates.iterator(); 2563 boolean isFuture = false; 2564 Date now = new Date(); 2565 2566 while (iterator.hasNext() && !isFuture) 2567 { 2568 Date date = iterator.next(); 2569 isFuture = date.after(now); 2570 2571 if (!isFuture) 2572 { 2573 Collection<String> pastPostIds = _sortedPostIds.get(date); 2574 for (String id : pastPostIds) 2575 { 2576 Post pastPost = _posts.get(id); 2577 if (pastPost != null) 2578 { 2579 pastPosts.add(pastPost); 2580 } 2581 } 2582 } 2583 } 2584 2585 for (Post pastPost : pastPosts) 2586 { 2587 removePost(pastPost); 2588 } 2589 2590 return pastPosts; 2591 } 2592 2593 /** 2594 * Test if a post exists, provided its ID. 2595 * @param id the post content ID. 2596 * @return true if the post exists. 2597 */ 2598 public Post getPost(String id) 2599 { 2600 return _posts.get(id); 2601 } 2602 2603 /** 2604 * Test if a post exists, provided its ID. 2605 * @param id the post content ID. 2606 * @return true if the post exists. 2607 */ 2608 public boolean hasPost(String id) 2609 { 2610 return _posts.containsKey(id); 2611 } 2612 2613 /** 2614 * Add a post to the cache. 2615 * @param content the post content. 2616 */ 2617 public void addPost(Content content) 2618 { 2619 CompositeMetadata meta = content.getMetadataHolder(); 2620 String id = content.getId(); 2621 2622 if (_posts.containsKey(id)) 2623 { 2624 removePost(content); 2625 } 2626 2627 Date date = meta.getDate("date"); 2628 2629 Post post = new Post(); 2630 post.setId(id); 2631 post.setName(content.getName()); 2632 post.setDate(meta.getDate("date")); 2633 post.setTags(meta.getStringArray("tags", new String[0])); 2634 2635 _posts.put(post.getId(), post); 2636 _sortedPostIds.put(date, id); 2637 } 2638 2639 /** 2640 * Remove a post from the cache. 2641 * @param content the post content to remove from the cache. 2642 */ 2643 public void removePost(Content content) 2644 { 2645 Post post = _posts.remove(content.getId()); 2646 if (post != null) 2647 { 2648 _sortedPostIds.remove(post.getDate(), post.getId()); 2649 } 2650 } 2651 2652 /** 2653 * Remove a post from the cache. 2654 * @param post the post content to remove from the cache. 2655 */ 2656 public void removePost(Post post) 2657 { 2658 _posts.remove(post.getId()); 2659 _sortedPostIds.remove(post.getDate(), post.getId()); 2660 } 2661 2662 /** 2663 * Clear the cache. 2664 */ 2665 public void clear() 2666 { 2667 _posts.clear(); 2668 _sortedPostIds.clear(); 2669 } 2670 } 2671 2672 /** 2673 * Reverse date post comparator. 2674 */ 2675 public class ReverseDatePostComparator implements Comparator<Post> 2676 { 2677 @Override 2678 public int compare(Post p1, Post p2) 2679 { 2680 if (p1.getDate() == null) 2681 { 2682 return 1; 2683 } 2684 if (p2.getDate() == null) 2685 { 2686 return -1; 2687 } 2688 return p2.getDate().compareTo(p1.getDate()); 2689 } 2690 } 2691 2692}