jp.aegif.nemaki.cmis.service.impl.VersioningServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for jp.aegif.nemaki.cmis.service.impl.VersioningServiceImpl.java

Source

/*******************************************************************************
 * Copyright (c) 2013 aegif.
 *
 * This file is part of NemakiWare.
 *
 * NemakiWare is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * NemakiWare 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 NemakiWare.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 *     linzhixing(https://github.com/linzhixing) - initial API and implementation
 ******************************************************************************/
package jp.aegif.nemaki.cmis.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.concurrent.locks.Lock;

import jp.aegif.nemaki.businesslogic.ContentService;
import jp.aegif.nemaki.cmis.aspect.CompileService;
import jp.aegif.nemaki.cmis.aspect.ExceptionService;
import jp.aegif.nemaki.cmis.service.VersioningService;
import jp.aegif.nemaki.model.Content;
import jp.aegif.nemaki.model.Document;
import jp.aegif.nemaki.model.VersionSeries;
import jp.aegif.nemaki.util.cache.NemakiCachePool;
import jp.aegif.nemaki.util.constant.DomainType;
import jp.aegif.nemaki.util.lock.ThreadLockService;

import org.apache.chemistry.opencmis.commons.data.Acl;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
import org.apache.chemistry.opencmis.commons.data.ObjectData;
import org.apache.chemistry.opencmis.commons.data.PermissionMapping;
import org.apache.chemistry.opencmis.commons.data.Properties;
import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
import org.apache.chemistry.opencmis.commons.server.CallContext;
import org.apache.chemistry.opencmis.commons.spi.Holder;
import org.apache.commons.lang.StringUtils;

public class VersioningServiceImpl implements VersioningService {
    private ContentService contentService;
    private CompileService compileService;
    private ExceptionService exceptionService;
    private NemakiCachePool nemakiCachePool;
    private ThreadLockService threadLockService;

    @Override
    /**
     * Repository only allow the latest version to be checked out
     */
    public void checkOut(CallContext callContext, String repositoryId, Holder<String> objectId,
            ExtensionsData extension, Holder<Boolean> contentCopied) {

        exceptionService.invalidArgumentRequiredHolderString("objectId", objectId);
        String originalId = objectId.getValue();

        Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());

        try {
            lock.lock();
            nemakiCachePool.get(repositoryId).removeCmisCache(originalId);
            // //////////////////
            // General Exception
            // //////////////////
            Document document = contentService.getDocument(repositoryId, objectId.getValue());
            exceptionService.objectNotFound(DomainType.OBJECT, document, objectId.getValue());
            exceptionService.permissionDenied(callContext, repositoryId, PermissionMapping.CAN_CHECKOUT_DOCUMENT,
                    document);

            // //////////////////
            // Specific Exception
            // //////////////////
            // CMIS doesn't define the error type when checkOut is performed
            // repeatedly
            exceptionService.constraintAlreadyCheckedOut(repositoryId, document);
            exceptionService.constraintVersionable(repositoryId, document.getObjectType());
            exceptionService.versioning(document);

            // //////////////////
            // Body of the method
            // //////////////////
            Document pwc = contentService.checkOut(callContext, repositoryId, objectId.getValue(), extension);
            objectId.setValue(pwc.getId());
            Holder<Boolean> copied = new Holder<Boolean>(true);
            contentCopied = copied;

        } finally {
            lock.unlock();
        }
    }

    @Override
    public void cancelCheckOut(CallContext callContext, String repositoryId, String objectId,
            ExtensionsData extension) {

        exceptionService.invalidArgumentRequiredString("objectId", objectId);

        Lock lock = threadLockService.getWriteLock(repositoryId, objectId);

        try {
            lock.lock();
            nemakiCachePool.get(repositoryId).removeCmisCache(objectId);
            // //////////////////
            // General Exception
            // //////////////////
            Document document = contentService.getDocument(repositoryId, objectId);
            exceptionService.objectNotFound(DomainType.OBJECT, document, objectId);
            exceptionService.permissionDenied(callContext, repositoryId, PermissionMapping.CAN_CHECKIN_DOCUMENT,
                    document);

            // //////////////////
            // Specific Exception
            // //////////////////
            exceptionService.constraintVersionable(repositoryId, document.getObjectType());

            // //////////////////
            // Body of the method
            // //////////////////
            contentService.cancelCheckOut(callContext, repositoryId, objectId, extension);

            //remove cache

            Document latest = contentService.getDocumentOfLatestVersion(repositoryId,
                    document.getVersionSeriesId());
            //Latest document does not exit when pwc is created as the first version
            if (latest != null) {
                Lock latestLock = threadLockService.getWriteLock(repositoryId, latest.getId());
                try {
                    latestLock.lock();
                    nemakiCachePool.get(repositoryId).removeCmisCache(latest.getId());
                } finally {
                    latestLock.unlock();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void checkIn(CallContext callContext, String repositoryId, Holder<String> objectId, Boolean major,
            Properties properties, ContentStream contentStream, String checkinComment, List<String> policies,
            Acl addAces, Acl removeAces, ExtensionsData extension) {

        exceptionService.invalidArgumentRequiredHolderString("objectId", objectId);

        Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());

        try {
            lock.lock();

            // //////////////////
            // General Exception
            // //////////////////

            Document pwc = contentService.getDocument(repositoryId, objectId.getValue());
            nemakiCachePool.get(repositoryId).removeCmisCache(pwc.getId());

            exceptionService.objectNotFound(DomainType.OBJECT, pwc, objectId.getValue());
            exceptionService.permissionDenied(callContext, repositoryId,
                    PermissionMapping.CAN_CANCEL_CHECKOUT_DOCUMENT, pwc);

            // //////////////////
            // Specific Exception
            // //////////////////
            exceptionService.constraintVersionable(repositoryId, pwc.getObjectType());
            // TODO implement
            // exceptionService.streamNotSupported(documentTypeDefinition,
            // contentStream);

            // //////////////////
            // Body of the method
            // //////////////////
            Document checkedIn = contentService.checkIn(callContext, repositoryId, objectId, major, properties,
                    contentStream, checkinComment, policies, addAces, removeAces, extension);
            objectId.setValue(checkedIn.getId());

            //refresh latest version
            Document latest = contentService.getDocumentOfLatestVersion(repositoryId, pwc.getVersionSeriesId());
            if (latest != null) {
                Lock latestLock = threadLockService.getWriteLock(repositoryId, latest.getId());
                try {
                    latestLock.lock();
                    nemakiCachePool.get(repositoryId).removeCmisCache(latest.getId());
                } finally {
                    latestLock.unlock();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    @Override
    public ObjectData getObjectOfLatestVersion(CallContext context, String repositoryId, String objectId,
            String versionSeriesId, Boolean major, String filter, Boolean includeAllowableActions,
            IncludeRelationships includeRelationships, String renditionFilter, Boolean includePolicyIds,
            Boolean includeAcl, ExtensionsData extension) {
        // //////////////////
        // General Exception
        // //////////////////
        // Chemistry Atompub calls this method only with objectId
        if (versionSeriesId == null) {
            exceptionService.invalidArgumentRequiredString("objectId", objectId);
            Document d = contentService.getDocument(repositoryId, objectId);
            versionSeriesId = d.getVersionSeriesId();
        }

        // Default to false
        Boolean _major = (major == null) ? false : major;
        Document document = null;
        if (_major) {
            document = contentService.getDocumentOfLatestMajorVersion(repositoryId, versionSeriesId);
        } else {
            document = contentService.getDocumentOfLatestVersion(repositoryId, versionSeriesId);
        }

        Lock lock = threadLockService.getReadLock(repositoryId, document.getId());

        try {
            lock.lock();

            exceptionService.objectNotFound(DomainType.OBJECT, document, versionSeriesId);
            exceptionService.permissionDenied(context, repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT,
                    document);

            // //////////////////
            // Body of the method
            // //////////////////
            ObjectData objectData = compileService.compileObjectData(context, repositoryId, document, filter,
                    includeAllowableActions, includeRelationships, renditionFilter, includeAcl);
            return objectData;

        } finally {
            lock.unlock();
        }
    }

    @Override
    public List<ObjectData> getAllVersions(CallContext context, String repositoryId, String objectId,
            String versionSeriesId, String filter, Boolean includeAllowableActions, ExtensionsData extension) {
        // //////////////////
        // General Exception
        // //////////////////
        // CMIS spec needs versionSeries as required, but Chemistry also takes
        // objectId
        if (StringUtils.isBlank(versionSeriesId)) {
            exceptionService.invalidArgumentRequiredString("objectId", objectId);
            Document d = contentService.getDocument(repositoryId, objectId);
            versionSeriesId = d.getVersionSeriesId();
        }

        List<Document> allVersions = contentService.getAllVersions(context, repositoryId, versionSeriesId);
        exceptionService.objectNotFoundVersionSeries(versionSeriesId, allVersions);

        List<Lock> locks = threadLockService.readLocks(repositoryId, allVersions);
        try {
            threadLockService.bulkLock(locks);

            // Sort by the descending order
            Collections.sort(allVersions, new VersionComparator());

            Document latest = allVersions.get(0);
            if (latest.isPrivateWorkingCopy()) {
                VersionSeries vs = contentService.getVersionSeries(repositoryId, latest);
                if (!context.getUsername().equals(vs.getVersionSeriesCheckedOutBy())) {
                    allVersions.remove(latest);
                }
            }

            // //////////////////
            // Body of the method
            // //////////////////
            List<ObjectData> result = new ArrayList<ObjectData>();
            for (Content content : allVersions) {
                ObjectData objectData = compileService.compileObjectData(context, repositoryId, content, filter,
                        includeAllowableActions, IncludeRelationships.NONE, null, true);
                result.add(objectData);
            }

            return result;

        } finally {
            threadLockService.bulkUnlock(locks);
        }
    }

    /**
     * Descending order by cmis:creationDate
     *
     * @author linzhixing
     */
    private class VersionComparator implements Comparator<Content> {
        @Override
        public int compare(Content content0, Content content1) {
            // TODO when created time is not set
            GregorianCalendar created0 = content0.getCreated();
            GregorianCalendar created1 = content1.getCreated();

            if (created0.before(created1)) {
                return 1;
            } else if (created0.after(created1)) {
                return -1;
            } else {
                return 0;
            }
        }
    }

    public void setContentService(ContentService contentService) {
        this.contentService = contentService;
    }

    public void setCompileService(CompileService compileService) {
        this.compileService = compileService;
    }

    public void setExceptionService(ExceptionService exceptionService) {
        this.exceptionService = exceptionService;
    }

    public void setNemakiCachePool(NemakiCachePool nemakiCachePool) {
        this.nemakiCachePool = nemakiCachePool;
    }

    public void setThreadLockService(ThreadLockService threadLockService) {
        this.threadLockService = threadLockService;
    }
}