com.edgenius.wiki.service.impl.SpaceServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.edgenius.wiki.service.impl.SpaceServiceImpl.java

Source

/* 
 * =============================================================
 * Copyright (C) 2007-2011 Edgenius (http://www.edgenius.com)
 * =============================================================
 * License Information: http://www.edgenius.com/licensing/edgenius/2.0/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2.0
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * http://www.gnu.org/licenses/gpl.txt
 *  
 * ****************************************************************
 */
package com.edgenius.wiki.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;

import com.edgenius.core.SecurityValues;
import com.edgenius.core.model.Role;
import com.edgenius.core.model.User;
import com.edgenius.core.repository.FileNode;
import com.edgenius.core.repository.ITicket;
import com.edgenius.core.repository.RepositoryException;
import com.edgenius.core.repository.RepositoryQuotaException;
import com.edgenius.core.repository.RepositoryService;
import com.edgenius.core.repository.RepositoryTiemoutExcetpion;
import com.edgenius.core.service.MessageService;
import com.edgenius.core.service.RoleService;
import com.edgenius.core.service.UserReadingService;
import com.edgenius.wiki.WikiConstants;
import com.edgenius.wiki.dao.DraftDAO;
import com.edgenius.wiki.dao.PageDAO;
import com.edgenius.wiki.dao.PageProgressDAO;
import com.edgenius.wiki.dao.PageTagDAO;
import com.edgenius.wiki.dao.SpaceDAO;
import com.edgenius.wiki.dao.TemplateDAO;
import com.edgenius.wiki.gwt.client.server.utils.SharedConstants;
import com.edgenius.wiki.model.Page;
import com.edgenius.wiki.model.PageContent;
import com.edgenius.wiki.model.PageProgress;
import com.edgenius.wiki.model.Space;
import com.edgenius.wiki.quartz.QuartzException;
import com.edgenius.wiki.quartz.RemoveSpaceJobInvoker;
import com.edgenius.wiki.security.service.SecurityService;
import com.edgenius.wiki.service.PageException;
import com.edgenius.wiki.service.PageService;
import com.edgenius.wiki.service.SpaceException;
import com.edgenius.wiki.service.SpaceService;
import com.edgenius.wiki.service.TagService;
import com.edgenius.wiki.util.PageComparator;
import com.edgenius.wiki.util.WikiUtil;

/**
 * @author Dapeng.Ni
 */
@Transactional
public class SpaceServiceImpl implements SpaceService {
    private static final Logger log = LoggerFactory.getLogger(SpaceServiceImpl.class);

    private SpaceDAO spaceDAO;

    private PageDAO pageDAO;

    private DraftDAO draftDAO;

    private PageTagDAO pageTagDAO;

    private PageProgressDAO pageProgressDAO;

    private SecurityService securityService;

    private MessageService messageService;

    private UserReadingService userReadingService;

    private TagService tagService;

    private RoleService roleService;
    private RepositoryService repositoryService;

    private PageService pageService;

    private RemoveSpaceJobInvoker removeSpaceJobInvoker;

    private TemplateDAO templateDAO;

    // ********************************************************************
    // Methods
    // ********************************************************************
    public Space getSpaceByUname(String spaceUname) {
        return spaceDAO.getByUname(spaceUname);
    }

    public Space getSpaceByTitle(String spaceUname) {
        return spaceDAO.getByTitle(spaceUname);
    }

    public Space getSpace(Integer uid) {
        return spaceDAO.get(uid);
    }

    //JDK1.6 @Override
    public Page getLastUpdatedPage(String spaceUname) {
        List<Page> list = pageDAO.getRecentPages(spaceUname, 0, 1, true);
        if (list == null || list.size() == 0)
            return null;
        else
            return list.get(0);
    }

    //JDK1.6 @Override
    public Map<Integer, Long> getAllSpacePagesCount() {
        return spaceDAO.getAllSpacePageCount();
    }

    //!!! The second input parameter is only for SpaceIndexInterceptor to check if do lucene index.
    public Space updateSpace(Space space, boolean updateIndex) {
        tagService.saveUpdateSpaceTag(space, space.getTagString());

        // update
        WikiUtil.setTouchedInfo(userReadingService, space);
        spaceDAO.saveOrUpdate(space);
        log.info("Space " + space.getUnixName() + " is update");
        return space;
    }

    /**
     * Do following during creating space.
     * <li>Create space database record.</li>
     * <li>Create Home page record.</li>
     * <li>Create repository of this space.</li>
     * <li>Initial security resource. All initialized permission, resource and
     * assign them to corresponding users/roles</li>
     * <li>Create space group role. Role contains space creator.</li>
     */
    public Page createSpace(Space space) throws SpaceException {
        try {
            // skip system default Resource name:
            if (space.getUnixName().equalsIgnoreCase(SharedConstants.INSTANCE_NAME))
                throw new SpaceException(SharedConstants.INSTANCE_NAME + " can not be space name.");
            if (space.getUnixName().startsWith(WikiConstants.CONST_NONSPACE_RESOURCE_PREFIX))
                throw new SpaceException(
                        "Space name can not start with " + WikiConstants.CONST_NONSPACE_RESOURCE_PREFIX);

            // save space first,
            spaceDAO.saveOrUpdate(space);
            // TODO: all using spaceUname?
            repositoryService.createWorkspace(space.getUnixName(), space.getUnixName(), space.getUnixName());

            tagService.saveUpdateSpaceTag(space, space.getTagString());

            // create default home page
            Page home = new Page();
            home.setTitle(messageService.getMessage(WikiConstants.I18N_HOME_PAGE_TITLE,
                    new String[] { space.getUnixName() }));
            PageContent content = new PageContent();

            home.setContent(content);
            home.setSpace(space);
            // some init value
            PageProgress pageProgress = new PageProgress();
            home.setPageProgress(pageProgress);
            pageProgressDAO.saveOrUpdate(pageProgress);

            home.setVersion(1);
            home.setPageUuid(WikiUtil.createPageUuid(space.getUnixName(), space.getUnixName(), space.getUnixName(),
                    repositoryService));
            // page.setUnixName(WikiUtil.getPageUnixname(page.getTitle()));
            WikiUtil.setTouchedInfo(userReadingService, home);
            pageDAO.saveOrUpdate(home);

            // space.setCrWorkspace(crWorkspace);
            // initial permission as well
            securityService.initResourcePermission(space);

            // create a new role for new space
            Role role = new Role();
            role.setDescription(""); //don't put any desc - so not look weird in AddGroup panel, which combine display name and desc.
            role.setDisplayName(SharedConstants.SPACE_ROLE_DEFAULT_PREFIX + space.getUnixName());
            role.setName(Role.SPACE_ROLE_PREFIX + space.getUnixName());
            role.setType(Role.TYPE_SPACE);
            Set<User> users = new HashSet<User>();
            role.setUsers(users);
            //must get from DB rather than get from User Cache!
            if (space.getCreator() != null && !space.getCreator().isAnonymous()) {
                User admin = userReadingService.getUser(space.getCreator().getUid());
                admin.getRoles().add(role);
                users.add(admin);
            }
            WikiUtil.setTouchedInfo(userReadingService, role);
            //space role won't put into Lucene role index
            roleService.saveRole(role);

            log.info("Space " + space.getUnixName() + " is created");
            return home;
        } catch (Exception e) {
            throw new SpaceException(e);
        }

    }

    public void uploadLogo(Space space, FileNode smallLogo, FileNode largeLogo) {
        try {
            ITicket ticket = repositoryService.login(space.getUnixName(), space.getUnixName(), space.getUnixName());

            if (!repositoryService.hasIdentifierNode(ticket, RepositoryService.TYPE_SPACE, space.getUnixName())) {
                //check if space identifier exist, create one if no
                repositoryService.createIdentifier(ticket, RepositoryService.TYPE_SPACE, space.getUnixName());
            }

            repositoryService.saveFile(ticket, smallLogo, false, false);
            repositoryService.saveFile(ticket, largeLogo, false, false);

            //update user portrait
            space.setLogoSmall(smallLogo.getNodeUuid());
            space.setLogoLarge(largeLogo.getNodeUuid());

            updateSpace(space, false);

        } catch (RepositoryException e) {
            log.error("Repository error ", e);
        } catch (RepositoryTiemoutExcetpion e) {
            log.error("Repository error ", e);
        } catch (RepositoryQuotaException e) {
            log.error("Repository error ", e);
        }

    }

    public Space saveHomepage(Space space, Page homepage) {
        space.setHomepage(homepage);
        spaceDAO.saveOrUpdate(space);
        return space;
    }

    public List<String> getAllSpaceUnames() {
        return spaceDAO.getAllSpaceUnames();
    }

    @Transactional(readOnly = true)
    public List<Space> getSpaces(User viewer, int fromItem, int returnSize, String sortBy, String filter,
            boolean sortByDesc) {
        List<Space> available = new ArrayList<Space>();

        // read more spaces than expected, because security filter will remove some
        boolean enough = false;
        int OVER_READ = 2;
        int start = fromItem;
        while (!enough) {
            List<Space> spaces = spaceDAO.getSpaces(start, returnSize * OVER_READ, sortBy, filter, sortByDesc);
            if (spaces == null || spaces.size() == 0)
                break;

            if (securityService.isAllowResourceAdmin(SharedConstants.INSTANCE_NAME,
                    SecurityValues.RESOURCE_TYPES.INSTANCE, viewer)) {
                //if system admin, then all space is visible, whatever it is private or removed.
                //this is normally at System admin-> list all space page
                available = new ArrayList<Space>(spaces);
                //remove system space as well - it always invisible!
                Space sysSpace = new Space();
                sysSpace.setUnixName(SharedConstants.SYSTEM_SPACEUNAME);
                available.remove(sysSpace);

                if (available.size() > returnSize) {
                    available = available.subList(0, returnSize);
                }
                return available;
            }

            //Please note: this filter out is harmful for pagination as fromItem is normally calculate by COUNT_PER_PAGE*PAGE_NUM
            //they are fixed number, however, startItem is for database but not values after filter. 
            for (Space space : spaces) {
                if (SharedConstants.SYSTEM_SPACEUNAME.equals(space.getUnixName()))
                    continue;

                if (!isReadableSpace(space, viewer)) {
                    log.info("User " + viewer.getUsername() + " does have enough permission to view space "
                            + space.getUnixName());
                    continue;
                }

                available.add(space);

                if (returnSize > 0 && available.size() >= returnSize) {
                    enough = true;
                    break; //break out current looping
                }

            }
            if (returnSize > 0)
                start += returnSize * OVER_READ;
            else
                break;
        }
        return available;
    }

    public Space removeSpaceInDelay(String spaceUname, int delayHours) throws SpaceException {
        Space removeSpace;
        if (delayHours <= 0) {
            //remove permanently immediately
            removeSpace = removeSpace(spaceUname, true);
        } else {
            try {
                removeSpaceJobInvoker.invokeJob(spaceUname, WikiUtil.getUserName(), delayHours);
                removeSpace = removeSpace(spaceUname, false);
                // TODO: get space admin users/roles and send out warning email
            } catch (QuartzException e) {
                log.error("Delete space in delay can not set up success. ", e);
                throw new SpaceException(e);
            }
        }
        return removeSpace;
    }

    public int undoRemoveSpace(String spaceUname) throws SpaceException {
        Space space = spaceDAO.getByUname(spaceUname);
        if (space == null) {
            throw new SpaceException("Unable to find given space to remove: " + spaceUname);
        }
        try {
            removeSpaceJobInvoker.cancelJob(spaceUname);
            space.setRemoved(false);
            WikiUtil.setTouchedInfo(userReadingService, space);
            spaceDAO.saveOrUpdate(space);
        } catch (QuartzException e) {
            log.error("Unable undo remove space " + spaceUname + " with exception ", e);
            throw new SpaceException(e);
        }
        return space.getUid();
    }

    /**
     * Do following during deleting space in permanently.
     * <li>Delete space database record.</li>
     * <li>Remove any draft for this space.</li>
     * <li>Delete all pages in this space.</li>
     * <li>Remove all page tags in this space.</li>
     * <li>Remove space group role.</li>
     * <li>Remove permissions, resources.</li>
     * <li>Remove repository.</li>
     * 
     * Remove space index job will do in SpaceIndexInterceptor class.
     */
    public Space removeSpace(String spaceUname, boolean persist) throws SpaceException {
        Space space = spaceDAO.getByUname(spaceUname);
        if (space == null) {
            throw new SpaceException("Unable to find given space to remove: " + spaceUname);
        }
        if (persist) {
            // if 2 step remove(1st mark remove flag, 2nd physical remove): need
            // open this comments.
            //         if (!space.isRemoved())
            //            throw new SpaceException("Space " + spaceUname + " must be trashed before physical removed.");

            log.info("Space {} is going to be removed permanently.", spaceUname);

            templateDAO.removeSpaceTemplates(space.getUid());

            // remove all draft: draft refer to space, if they do not removed, space can not removed.
            // and draft remove must before page remove as draft refer to page in parent field.
            draftDAO.removeSpaceDrafts(space.getUid());
            // TODO: it is must very slow action: delete page by page. But it is
            // complex as well if bulk delete. There are
            // too many relation table, such as page process, Page
            List<Page> pages = pageDAO.getSpaceAllPages(spaceUname);

            //delete children first 
            TreeSet<Page> sortedSet = new TreeSet<Page>(new PageComparator(null));
            sortedSet.addAll(pages);

            pages = new ArrayList<Page>(sortedSet);
            Collections.reverse(pages);
            for (Page page : pages) {
                try {
                    pageService.removePage(spaceUname, page.getPageUuid(), false, persist);
                } catch (PageException e) {
                    log.error("Remove space " + spaceUname + " page failed.Title " + page.getTitle() + ". UUID:"
                            + page.getPageUuid(), e);
                }
            }

            try {
                ITicket fromTicket = repositoryService.login(spaceUname, spaceUname, spaceUname);
                repositoryService.removeWorkspace(fromTicket, spaceUname);
            } catch (RepositoryException e) {
                log.error("Unable remove space " + spaceUname + "'s repository stuff ", e);
                throw new SpaceException("Unable remove space " + spaceUname + "'s repository stuff " + e);
            } catch (RepositoryTiemoutExcetpion e) {
                log.error("Unable remove space " + spaceUname + "'s repository stuff ", e);
                throw new SpaceException("Unable remove space " + spaceUname + "'s repository stuff " + e);
            }

            //before resource delete, get this space admin user mail list so that it could send out notification.
            space.setAdminMailList(userReadingService.getSpaceAdminMailList(spaceUname));

            //remove from DB
            pageTagDAO.removeTagsInSpace(spaceUname);
            roleService.removeRole(Role.SPACE_ROLE_PREFIX + spaceUname);
            securityService.removeResource(spaceUname);
            spaceDAO.removeObject(space);

            log.info("Space " + spaceUname + " is removed permanently.");
        } else {
            log.info("Space " + spaceUname + " is marked as removed.");
            space.setRemoved(true);
            //delay get admin user mail list until send mail Notify program
            space.setAdminMailList(null);
            WikiUtil.setTouchedInfo(userReadingService, space);
            spaceDAO.saveOrUpdate(space);
        }

        return space;

    }

    public List<Page> getRemovedCurrentPages(String spaceUname) {

        return pageDAO.getRemovedPagesInSpace(spaceUname);
    }

    @Transactional(readOnly = true)
    public List<Page> getRecentPages(String spaceUname, int retCount, boolean sortByModify) {
        // return 10 records of latest updated
        // just get given space's recently pages
        return pageDAO.getRecentPages(spaceUname, 0, retCount, sortByModify);
    }

    public List<Page> getRecentPages(String spaceUname, int start, int retCount, boolean sortByModify) {
        List<Page> pages = pageDAO.getRecentPages(spaceUname, start, retCount, sortByModify);
        for (Page page : pages) {
            page.getContent().getContent();
            page.getSpace().getUnixName();
        }
        return pages;
    }

    public List<Space> getUserAllCreatedSpaces(String username, int limit, User viewer) {
        List<Space> list = spaceDAO.getUserCreatedSpaces(username, limit);
        if (list != null && viewer != null) {
            // filter out the page which is not allow viewer to read
            for (Iterator<Space> iter = list.iterator(); iter.hasNext();) {
                Space space = iter.next();
                if (!isReadableSpace(space, viewer)) {
                    iter.remove();
                }
            }
        }
        return list;
    }

    public List<FileNode> getAttachments(String spaceUname, boolean withHistory, boolean withDraft, User viewer)
            throws RepositoryException {

        //just compare nodeUuid(file name could change, cannot comparable), if same, compare nodeVersion then.
        Set<FileNode> set = new TreeSet<FileNode>(new Comparator<FileNode>() {
            public int compare(FileNode o1, FileNode o2) {
                if (o1.getNodeUuid().equals(o2.getNodeUuid())) {
                    //from large to small
                    return o2.getVersion().compareTo(o1.getVersion());
                } else
                    return o1.getNodeUuid().compareTo(o2.getNodeUuid());
            }

        });
        ITicket ticket = repositoryService.login(spaceUname, spaceUname, spaceUname);
        //don't get file stream, only get necessary description information
        List<FileNode> atts = repositoryService.getAllSpaceNodes(ticket, RepositoryService.TYPE_ATTACHMENT,
                spaceUname, false);

        for (FileNode node : atts) {
            if (node.getStatus() > 0) {
                if (!withDraft) {
                    continue;
                } else if (viewer == null
                        || !StringUtils.equalsIgnoreCase(node.getCreateor(), viewer.getUsername())) {
                    //viewer is anonymous or the attachment is uploaded by this viewer, skip
                    continue;
                }
            }
            String username = node.getCreateor();
            User user = userReadingService.getUserByName(username);
            //pass back user fullname
            node.setUserFullname(user.getFullname());
            set.add(node);
        }
        if (!withHistory) {
            //remove history version
            List<String> nodeUuids = new ArrayList<String>();
            for (Iterator<FileNode> iter = set.iterator(); iter.hasNext();) {
                FileNode node = iter.next();
                String uuid = node.getNodeUuid();
                if (nodeUuids.contains(uuid)) {
                    iter.remove();
                    continue;
                }
                nodeUuids.add(uuid);
            }
        }
        return new ArrayList<FileNode>(set);
    }

    //JDK1.6 @Override
    public int getUserAuthoredSpaceSize(String username) {
        return spaceDAO.getUserAuthoredSize(username);
    }

    public Space getSystemSpace() {
        return spaceDAO.getSystemSpace();
    }

    public int getSpaceCount(String filter) {
        return spaceDAO.getSpaceCount(filter);
    }

    public int getRemovedSpaceLeftHours(String spaceUname) {
        return removeSpaceJobInvoker.getLeftHours(spaceUname);
    }

    // ********************************************************************
    // Private
    // ********************************************************************
    /**
     * Check if space can be view by given viewer. 
     */
    private boolean isReadableSpace(Space space, User viewer) {
        if (space.isRemoved())
            return false;

        // only private space need check if this space allow read or
        // not, for public space, space is always can be list.
        if (space.isPrivate()) {
            if (!securityService.isAllowSpaceReading(space.getUnixName(), viewer)) {
                if (log.isDebugEnabled())
                    log.debug("Space {} is private and does not allow read.", space.getUnixName());
                return false;
            }
        }

        return true;
    }

    // ********************************************************************
    // Set / Get
    // ********************************************************************
    public void setSpaceDAO(SpaceDAO spaceDAO) {
        this.spaceDAO = spaceDAO;
    }

    public void setSecurityService(SecurityService securityService) {
        this.securityService = securityService;
    }

    public void setMessageService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void setUserReadingService(UserReadingService userReadingService) {
        this.userReadingService = userReadingService;
    }

    public void setPageDAO(PageDAO pageDAO) {
        this.pageDAO = pageDAO;
    }

    public void setTemplateDAO(TemplateDAO templateDAO) {
        this.templateDAO = templateDAO;
    }

    public void setRepositoryService(RepositoryService repositoryService) {
        this.repositoryService = repositoryService;
    }

    public void setPageService(PageService pageService) {
        this.pageService = pageService;
    }

    public void setPageProgressDAO(PageProgressDAO pageProgressDAO) {
        this.pageProgressDAO = pageProgressDAO;
    }

    public void setPageTagDAO(PageTagDAO pageTagDAO) {
        this.pageTagDAO = pageTagDAO;
    }

    public void setRemoveSpaceJobInvoker(RemoveSpaceJobInvoker removeSpaceJobInvoker) {
        this.removeSpaceJobInvoker = removeSpaceJobInvoker;
    }

    public void setDraftDAO(DraftDAO draftDAO) {
        this.draftDAO = draftDAO;
    }

    public void setRoleService(RoleService roleService) {
        this.roleService = roleService;
    }

    public void setTagService(TagService tagService) {
        this.tagService = tagService;
    }

}