/* * Copyright (C) 2014 Toshiaki Maki * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package am.ik.categolj2.domain.service.entry; import am.ik.categolj2.core.message.MessageKeys; import am.ik.categolj2.domain.model.Category; import am.ik.categolj2.domain.model.Entry; import am.ik.categolj2.domain.model.EntryHistory; import am.ik.categolj2.domain.model.Tag; import am.ik.categolj2.domain.repository.category.CategoryRepository; import am.ik.categolj2.domain.repository.entry.EntryHistoryRepository; import am.ik.categolj2.domain.repository.entry.EntryRepository; import am.ik.categolj2.domain.repository.tag.TagAndEntryId; import am.ik.categolj2.domain.repository.tag.TagRepository; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.common.collect.TreeMultimap; import lombok.extern.slf4j.Slf4j; import org.dozer.Mapper; import org.joda.time.DateTime; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; import org.terasoluna.gfw.common.date.DateFactory; import org.terasoluna.gfw.common.exception.ResourceNotFoundException; import org.terasoluna.gfw.common.message.ResultMessages; import javax.inject.Inject; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @Service @Slf4j public class EntryServiceImpl implements EntryService { @Inject EntryRepository entryRepository; @Inject EntryHistoryRepository entryHistoryRepository; @Inject CategoryRepository categoryRepository; @Inject TagRepository tagRepository; @Inject Mapper beanMapper; @Inject DateFactory dateFactory; Pageable recentPageable = new PageRequest(0, 10); @Override public Entry findOne(Integer entryId) { Entry entry = entryRepository.findDetails(entryId); if (entry == null) { ResultMessages messages = ResultMessages.error() .add(MessageKeys.E_CT_EN_8201, entryId); throw new ResourceNotFoundException(messages); } return entry; } @Override @Cacheable("entry") public Entry findOnePublished(Integer entryId) { Entry entry = entryRepository.findDetailsPublished(entryId); if (entry == null) { ResultMessages messages = ResultMessages.error() .add(MessageKeys.E_CT_EN_8201, entryId); throw new ResourceNotFoundException(messages); } return entry; } @Override public Page<Entry> findPage(Pageable pageable) { Page<Entry> page = entryRepository .findPageOrderByLastModifiedDateDesc(pageable); applyRelations(page); return page; } void applyRelations(Iterable<Entry> entries) { List<Integer> entryIds = StreamSupport.stream(entries.spliterator(), false) .map(Entry::getEntryId) .collect(Collectors.toList()); if (!entryIds.isEmpty()) { // apply categories List<Category> categories = categoryRepository.findByEntryIds(entryIds); Multimap<Integer, Category> categoryMultimap = TreeMultimap.create(); for (Category c : categories) { categoryMultimap.put(c.getEntry().getId(), c); } for (Entry entry : entries) { entry.setCategory(new ArrayList<>(categoryMultimap.get(entry.getId()))); } // apply tags List<TagAndEntryId> tags = tagRepository.findByEntryIds(entryIds); Multimap<Integer, Tag> tagMultimap = HashMultimap.create(); for (TagAndEntryId tag : tags) { tagMultimap.put(tag.getEntryId(), tag.getTag()); } for (Entry entry : entries) { entry.setTags(new LinkedHashSet<>(tagMultimap.get(entry.getEntryId()))); } } } @Override @Cacheable("entry") public Page<Entry> findPagePublished(Pageable pageable) { Page<Entry> page = entryRepository .findPagePublishedOrderByLastModifiedDateDesc(pageable); applyRelations(page); return page; } @Override @Cacheable("recentPost") public List<Entry> findAllPublishedUpdatedRecently() { List<Entry> entries = entryRepository .findAllPublishedOrderByLastModifiedDateDesc(recentPageable); return entries; } @Override public Page<Entry> findPagePublishedByCategoryNameAndCategoryOrder( String categoryName, Integer categoryOrder, Pageable pageable) { Page<Entry> page = entryRepository.findPageDetailsPublishedByCategoryNameAndOrder( categoryName, categoryOrder, pageable); applyRelations(page); return page; } @Override public Page<Entry> findPagePublishedByCreatedBy(String createdBy, Pageable pageable) { Page<Entry> page = entryRepository.findPagePublishedByCreatedBy(createdBy, pageable); applyRelations(page); return page; } @Override public Page<Entry> findPagePublishedByTagName(String tagName, Pageable pageable) { Page<Entry> page = entryRepository.findPageByTags_TagNameAndPublishedTrueOrderByLastModifiedDateDesc(tagName, pageable); applyRelations(page); return page; } @Override public Page<Entry> findPageByTagName(String tagName, Pageable pageable) { Page<Entry> page = entryRepository.findPageByTags_TagNameOrderByLastModifiedDateDesc(tagName, pageable); applyRelations(page); return page; } @Override public Page<Entry> searchPageByKeyword(String keyword, Pageable pageable) { Page<Entry> page = entryRepository.searchPageByKeyword(keyword, pageable); applyRelations(page); return page; } @Override public Page<Entry> searchPagePublishedByKeyword(String keyword, Pageable pageable) { Page<Entry> page = entryRepository.searchPagePublishedByKeyword(keyword, pageable); applyRelations(page); return page; } @Override @Transactional @CacheEvict(value = {"recentPost", "entry"}, allEntries = true) public Entry create(Entry entry, List<Category> category) { Assert.notNull(entry, "entry must not be null"); Assert.isNull(entry.getCategory(), "entry.category must be null"); Assert.notNull(category, "category must not be null or empty"); Assert.notNull(entry.getTags(), "category must not be null"); // create new tags tagRepository.save(entry.getTags().stream() .filter(tag -> !tagRepository.exists(tag.getTagName())) .collect(Collectors.toList())); DateTime now = dateFactory.newDateTime(); entry.setCreatedDate(now); entry.setLastModifiedDate(now); // createdBy,lastModifiedBy are set by AuditingEntityListener entryRepository.saveAndFlush(entry); Integer entryId = entry.getEntryId(); for (Category c : category) { c.getCategoryPK().setEntryId(entryId); } entry.setCategory(category); return entry; } @Override @Transactional @CacheEvict(value = {"recentPost", "entry"}, allEntries = true) public Entry update(Integer entryId, Entry updatedEntry, boolean updateLastModifiedDate, boolean saveInHistory) { Assert.notNull(updatedEntry, "entry must not be null"); Entry entry = findOne(entryId); // old entry if (saveInHistory) { log.info("save history for entryId={}", entryId); EntryHistory history = beanMapper.map(entry, EntryHistory.class); history.setEntry(entry); entryHistoryRepository.save(history); } if (updateLastModifiedDate) { DateTime now = dateFactory.newDateTime(); updatedEntry.setLastModifiedDate(now); } Set<Tag> reduced = Sets.difference(entry.getTags(), updatedEntry.getTags()); houseKeepTag(reduced); // copy new values to entry beanMapper.map(updatedEntry, entry); entryRepository.save(entry); return entry; } @Override @Transactional @CacheEvict(value = {"recentPost", "entry"}, allEntries = true) public void delete(Integer entryId) { Entry entry = findOne(entryId); houseKeepTag(entry.getTags()); entryRepository.delete(entry); } void houseKeepTag(Set<Tag> tags) { tagRepository.delete(tags.stream() .filter(tag -> tagRepository.countByTagName(tag.getTagName()) == 1) .peek(tag -> log.info("Delete unused tag -> {}", tag)) .collect(Collectors.toList())); } }