com.wooki.domain.biz.ChapterManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.wooki.domain.biz.ChapterManagerImpl.java

Source

//
// Copyright 2009 Robin Komiwes, Bruno Verachten, Christophe Cordenier
//
// 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 com.wooki.domain.biz;

import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;

import com.ibm.icu.util.Calendar;
import com.wooki.Draft;
import com.wooki.domain.dao.ActivityDAO;
import com.wooki.domain.dao.ChapterDAO;
import com.wooki.domain.dao.CommentDAO;
import com.wooki.domain.dao.PublicationDAO;
import com.wooki.domain.exception.AuthorizationException;
import com.wooki.domain.exception.PublicationXmlException;
import com.wooki.domain.model.Chapter;
import com.wooki.domain.model.Comment;
import com.wooki.domain.model.CommentState;
import com.wooki.domain.model.Publication;
import com.wooki.domain.model.User;
import com.wooki.domain.model.activity.Activity;
import com.wooki.domain.model.activity.ChapterActivity;
import com.wooki.domain.model.activity.ChapterEventType;
import com.wooki.domain.model.activity.CommentActivity;
import com.wooki.domain.model.activity.CommentEventType;
import com.wooki.services.parsers.DOMManager;
import com.wooki.services.security.WookiSecurityContext;

public class ChapterManagerImpl extends AbstractManager implements ChapterManager {
    private final ChapterDAO chapterDao;

    private final ActivityDAO activityDao;

    private final CommentDAO commentDao;

    private final PublicationDAO publicationDao;

    private final DOMManager domManager;

    private WookiSecurityContext securityCtx;

    private Logger logger = LoggerFactory.getLogger(ChapterManagerImpl.class);

    private Map<Long, ReentrantLock> locks = CollectionFactory.newConcurrentMap();

    public ChapterManagerImpl(ChapterDAO chapterDAO, ActivityDAO activityDAO, CommentDAO commentDAO,
            PublicationDAO publicationDAO, DOMManager domManager, ApplicationContext context) {
        this.chapterDao = chapterDAO;
        this.activityDao = activityDAO;
        this.commentDao = commentDAO;
        this.publicationDao = publicationDAO;
        this.domManager = domManager;

        this.securityCtx = context.getBean(WookiSecurityContext.class);
    }

    public Comment addComment(Long publicationId, String content, String domId) {

        // Check security
        if (!securityCtx.isLoggedIn()) {
            throw new AuthorizationException("Only logged user are allowed to add a comments.");
        }

        User author = securityCtx.getUser();

        if (publicationId == null || content == null) {
            throw new IllegalArgumentException("Chapter and comment cannot be null for addition.");
        }

        Publication toUpdate = publicationDao.findById(publicationId);
        Comment comment = new Comment();
        comment.setState(CommentState.OPEN);
        comment.setCreationDate(new Date());
        comment.setDomId(domId);
        comment.setUser(author);
        comment.setContent(content);
        comment.setPublication(toUpdate);
        toUpdate.addComment(comment);
        this.commentDao.create(comment);

        // Log activity
        CommentActivity activity = new CommentActivity();
        activity.setBook(toUpdate.getChapter().getBook());
        activity.setChapter(toUpdate.getChapter());
        activity.setCreationDate(Calendar.getInstance().getTime());
        activity.setComment(comment);
        activity.setType(CommentEventType.POST);
        activity.setUser(author);
        this.activityDao.create(activity);

        return comment;
    }

    public Chapter findById(Long chapterId) {
        return this.chapterDao.findById(chapterId);
    }

    public boolean isPublished(Long revision) {
        return this.publicationDao.isPublished(revision);
    }

    public Publication getRevision(Long chapterId, String revision) {
        assert chapterId != null;
        if (LAST.equalsIgnoreCase(revision) || revision == null) {
            return publicationDao.findLastRevision(chapterId);
        }
        try {
            return this.publicationDao.findRevisionById(chapterId, Long.parseLong(revision));
        } catch (NumberFormatException nfEx) {
            throw new IllegalArgumentException("Revision number is invalid");
        }
    }

    public String getLastContent(Long chapterId) {
        Publication publication = getRevision(chapterId, null);
        if (publication != null) {
            return publication.getContent();
        }
        return null;
    }

    public Publication getLastPublishedPublication(Long chapterId) {
        assert chapterId != null;
        return publicationDao.findLastPublishedRevision(chapterId);
    }

    public String getLastPublishedContent(Long chapterId) {
        Publication published = getLastPublishedPublication(chapterId);
        if (published != null) {
            return published.getContent();
        }
        return null;
    }

    public Chapter publishChapter(Long chapterId) {

        assert chapterId != null;

        Publication published = publicationDao.findLastRevision(chapterId);
        Chapter chapter = this.chapterDao.findById(chapterId);

        if (chapter == null || published == null) {
            throw new IllegalArgumentException("Cannot find chapter with id " + chapterId);
        }

        // Check security
        if (!securityCtx.isLoggedIn() || !this.securityCtx.canWrite(chapter.getBook())) {
            throw new AuthorizationException("Publish action not authorized");
        }

        // Adapt content
        String content = null;
        try {
            content = domManager.adaptContent(published.getContent(), published.getId());
        } catch (PublicationXmlException pxEx) {
            logger.error("Unable to publish document", pxEx);
            throw pxEx;
        }

        // Check that the logged user is an author of the book
        User author = securityCtx.getUser();

        // Flag last publication as not published
        Publication lastPublished = publicationDao.findLastPublishedRevision(chapterId);
        if (lastPublished != null) {
            lastPublished.setPublished(false);
            publicationDao.update(lastPublished);
        }

        // Publish the last revision
        published.setLastModified(Calendar.getInstance().getTime());
        published.setContent(content);
        published.setPublished(true);
        published.setChapter(chapter);

        publicationDao.update(published);

        ChapterActivity ca = new ChapterActivity();
        ca.setBook(chapter.getBook());
        ca.setChapter(published.getChapter());
        ca.setType(ChapterEventType.PUBLISHED);
        ca.setCreationDate(Calendar.getInstance().getTime());
        ca.setUser(author);
        activityDao.create(ca);

        return chapter;

    }

    public void restoreRevision(Long chapterId, String revision) {
        assert chapterId != null;

        Publication toRestore = getRevision(chapterId, revision);
        Chapter chapter = findById(chapterId);
        toRestore.getContent();
        Draft draft = new Draft();
        draft.setData(toRestore.getContent());
        draft.setTimestamp(chapter.getLastModified());
        updateAndPublishContent(chapterId, draft);
    }

    public void deleteRevision(Long chapterId, String revision) {
        assert chapterId != null;
        List<Publication> publications = publicationDao.listPublication(chapterId);
        if (publications.size() <= 1) {
            throw new IllegalStateException("Chapter must be associated at least to one revision");
        }

        Publication toDelete = getRevision(chapterId, revision);
        if (toDelete.isPublished()) {
            throw new IllegalStateException("Current published revision cannot be deleted");
        }

        publicationDao.delete(toDelete);
    }

    public Chapter update(Chapter chapter) {
        assert chapter != null;
        return this.chapterDao.update(chapter);
    }

    public void updateContent(Long chapterId, Draft draft) {

        assert chapterId != null;
        assert draft != null;
        assert draft.getData() != null;

        // Update last modified timestamp
        Date lastModified = Calendar.getInstance().getTime();
        ReentrantLock lock = getOrCreateLock(chapterId);

        Publication publication = publicationDao.findLastRevision(chapterId);
        lock.lock();

        Chapter chapter = null;

        try {
            chapter = chapterDao.findById(chapterId);
            if (chapter.getLastModified() != null && !chapter.getLastModified().equals(draft.getTimestamp())) {
                throw new ConcurrentModificationException(
                        "Document has been modified by another user in the meantime.");
            }
            chapter.setLastModified(lastModified);
            chapterDao.update(chapter);
        } finally {
            lock.unlock();
        }

        // we check the published flag. If set, then this Publication must
        // be considered as "locked" and we must create a new publication as
        // the new working copy
        if (publication == null || (publication != null && publication.isPublished())) {
            publication = new Publication();

            // Security check
            if (!securityCtx.isLoggedIn() || !this.securityCtx.canWrite(chapter.getBook())) {
                throw new AuthorizationException("Publish action not authorized");
            }
            publication.setChapter(chapter);

            publication.setCreationDate(Calendar.getInstance().getTime());
            publicationDao.create(publication);
        }

        publication.setContent(draft.getData());
        publication.setLastModified(lastModified);
        publicationDao.update(publication);

    }

    public void updateAndPublishContent(Long chapterId, Draft draft) {
        updateContent(chapterId, draft);
        publishChapter(chapterId);
    }

    public Chapter remove(Long chapterId) {
        assert chapterId != null;

        Chapter toDelete = chapterDao.findById(chapterId);

        if (toDelete == null) {
            throw new IllegalArgumentException("Cannot find chapter with id " + chapterId);
        }

        if (toDelete != null) {
            // The logged user must be allow to write to the book to delete a chapter in it
            if (!securityCtx.isLoggedIn() || !this.securityCtx.canWrite(toDelete.getBook())) {
                throw new AuthorizationException("Delete action not authorized");
            }

            chapterDao.delete(toDelete);

            ChapterActivity activity = new ChapterActivity();
            activity.setBook(toDelete.getBook());
            activity.setCreationDate(Calendar.getInstance().getTime());
            activity.setChapter(toDelete);
            activity.setUser(this.securityCtx.getUser());
            activity.setType(ChapterEventType.DELETE);
            this.activityDao.create(activity);

            List<Activity> activities = this.activityDao.listAllActivitiesOnChapter(chapterId);
            if (activities != null) {
                for (Activity ac : activities) {
                    ac.setResourceUnavailable(true);
                    this.activityDao.update(ac);
                }
            }

            // TODO Delete publication entries also ??

        }

        return toDelete;

    }

    public Object[] findPrevious(Long bookId, Long chapterId) {
        List<Object[]> result = this.chapterDao.findPrevious(bookId, chapterId);
        if (result != null && result.size() > 0) {
            return result.get(0);
        }
        return null;
    }

    public Object[] findNext(Long bookId, Long chapterId) {
        List<Object[]> result = this.chapterDao.findNext(bookId, chapterId);
        if (result != null && result.size() > 0) {
            return result.get(0);
        }
        return null;
    }

    public List<Chapter> listChapters(Long bookId) {
        assert bookId != null;
        return chapterDao.listChapters(bookId);
    }

    public List<Chapter> listChaptersInfo(Long bookId) {
        assert bookId != null;
        return chapterDao.listChapterInfo(bookId);
    }

    public List<Publication> listPublicationInfo(Long chapterId) {
        assert chapterId != null;
        return publicationDao.listPublication(chapterId);
    }

    private synchronized ReentrantLock getOrCreateLock(Long chapterId) {
        assert chapterId != null;

        if (locks.containsKey(chapterId)) {
            return locks.get(chapterId);
        }

        ReentrantLock lock = new ReentrantLock();
        locks.put(chapterId, lock);
        return lock;
    }

}