org.alfresco.rest.api.impl.RenditionsImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.rest.api.impl.RenditionsImpl.java

Source

/*
 * #%L
 * Alfresco Remote API
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

package org.alfresco.rest.api.impl;

import org.alfresco.model.ContentModel;
import org.alfresco.query.PagingResults;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.thumbnail.ThumbnailDefinition;
import org.alfresco.repo.thumbnail.ThumbnailHelper;
import org.alfresco.repo.thumbnail.ThumbnailRegistry;
import org.alfresco.repo.thumbnail.script.ScriptThumbnailService;
import org.alfresco.rest.antlr.WhereClauseParser;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.Renditions;
import org.alfresco.rest.api.model.ContentInfo;
import org.alfresco.rest.api.model.Rendition;
import org.alfresco.rest.api.model.Rendition.RenditionStatus;
import org.alfresco.rest.framework.core.exceptions.ApiException;
import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException;
import org.alfresco.rest.framework.core.exceptions.DisabledServiceException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.NotFoundException;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.content.CacheDirective;
import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
import org.alfresco.rest.framework.resource.content.FileBinaryResource;
import org.alfresco.rest.framework.resource.content.NodeBinaryResource;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.resource.parameters.where.Query;
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.thumbnail.ThumbnailService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;

import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * @author Jamal Kaabi-Mofrad
 */
public class RenditionsImpl implements Renditions, ResourceLoaderAware {
    private static final Log LOGGER = LogFactory.getLog(RenditionsImpl.class);

    private static final Set<String> RENDITION_STATUS_COLLECTION_EQUALS_QUERY_PROPERTIES = Collections
            .singleton(PARAM_STATUS);

    private Nodes nodes;
    private NodeService nodeService;
    private ThumbnailService thumbnailService;
    private ScriptThumbnailService scriptThumbnailService;
    private RenditionService renditionService;
    private MimetypeService mimetypeService;
    private ActionService actionService;
    private NamespaceService namespaceService;
    private ServiceRegistry serviceRegistry;
    private ResourceLoader resourceLoader;
    private TenantService tenantService;

    public void setNodes(Nodes nodes) {
        this.nodes = nodes;
    }

    public void setThumbnailService(ThumbnailService thumbnailService) {
        this.thumbnailService = thumbnailService;
    }

    public void setScriptThumbnailService(ScriptThumbnailService scriptThumbnailService) {
        this.scriptThumbnailService = scriptThumbnailService;
    }

    public void setServiceRegistry(ServiceRegistry serviceRegistry) {
        this.serviceRegistry = serviceRegistry;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public void setTenantService(TenantService tenantService) {
        this.tenantService = tenantService;
    }

    public void init() {
        PropertyCheck.mandatory(this, "nodes", nodes);
        PropertyCheck.mandatory(this, "thumbnailService", thumbnailService);
        PropertyCheck.mandatory(this, "scriptThumbnailService", scriptThumbnailService);
        PropertyCheck.mandatory(this, "serviceRegistry", serviceRegistry);
        PropertyCheck.mandatory(this, "tenantService", tenantService);

        this.nodeService = serviceRegistry.getNodeService();
        this.actionService = serviceRegistry.getActionService();
        this.renditionService = serviceRegistry.getRenditionService();
        this.mimetypeService = serviceRegistry.getMimetypeService();
        this.namespaceService = serviceRegistry.getNamespaceService();
    }

    @Override
    public CollectionWithPagingInfo<Rendition> getRenditions(String nodeId, Parameters parameters) {
        final NodeRef nodeRef = validateSourceNode(nodeId);
        String contentMimeType = getMimeType(nodeRef);

        Query query = parameters.getQuery();
        boolean includeCreated = true;
        boolean includeNotCreated = true;
        if (query != null) {
            // Filtering via "where" clause
            MapBasedQueryWalker propertyWalker = new MapBasedQueryWalker(
                    RENDITION_STATUS_COLLECTION_EQUALS_QUERY_PROPERTIES, null);
            QueryHelper.walk(query, propertyWalker);

            String withStatus = propertyWalker.getProperty(PARAM_STATUS, WhereClauseParser.EQUALS);
            if (withStatus != null) {
                try {
                    includeCreated = RenditionStatus.CREATED.equals(RenditionStatus.valueOf(withStatus));
                } catch (IllegalArgumentException ex) {
                    throw new InvalidArgumentException("Invalid status value: " + withStatus);
                }
                includeNotCreated = !includeCreated;
            }
        }

        Map<String, Rendition> apiRenditions = new TreeMap<>();
        if (includeNotCreated) {
            // List all available thumbnail definitions
            List<ThumbnailDefinition> thumbnailDefinitions = thumbnailService.getThumbnailRegistry()
                    .getThumbnailDefinitions(contentMimeType, -1);
            for (ThumbnailDefinition thumbnailDefinition : thumbnailDefinitions) {
                apiRenditions.put(thumbnailDefinition.getName(), toApiRendition(thumbnailDefinition));
            }
        }

        List<ChildAssociationRef> nodeRefRenditions = renditionService.getRenditions(nodeRef);
        if (!nodeRefRenditions.isEmpty()) {
            for (ChildAssociationRef childAssociationRef : nodeRefRenditions) {
                NodeRef renditionNodeRef = childAssociationRef.getChildRef();
                Rendition apiRendition = toApiRendition(renditionNodeRef);
                if (includeCreated) {
                    // Replace/append any thumbnail definitions with created rendition info
                    apiRenditions.put(apiRendition.getId(), apiRendition);
                } else {
                    // Remove any thumbnail definitions that has been created from the list,
                    // as the filter requires only the Not_Created renditions
                    apiRenditions.remove(apiRendition.getId());
                }
            }
        }

        // Wrap paging info, as the core service doesn't support paging
        Paging paging = parameters.getPaging();
        PagingResults<Rendition> results = Util.wrapPagingResults(paging, apiRenditions.values());

        return CollectionWithPagingInfo.asPaged(paging, results.getPage(), results.hasMoreItems(),
                results.getTotalResultCount().getFirst());
    }

    @Override
    public Rendition getRendition(String nodeId, String renditionId, Parameters parameters) {
        final NodeRef nodeRef = validateSourceNode(nodeId);
        NodeRef renditionNodeRef = getRenditionByName(nodeRef, renditionId, parameters);

        // if there is no rendition, then try to find the available/registered rendition (yet to be created).
        if (renditionNodeRef == null) {
            ThumbnailDefinition thumbnailDefinition = thumbnailService.getThumbnailRegistry()
                    .getThumbnailDefinition(renditionId);
            if (thumbnailDefinition == null) {
                throw new NotFoundException(renditionId + " is not registered.");
            } else {
                String contentMimeType = getMimeType(nodeRef);
                // List all available thumbnail definitions for the source node
                List<ThumbnailDefinition> thumbnailDefinitions = thumbnailService.getThumbnailRegistry()
                        .getThumbnailDefinitions(contentMimeType, -1);
                boolean found = false;
                for (ThumbnailDefinition td : thumbnailDefinitions) {
                    // Check the registered renditionId is applicable for the node's mimeType
                    if (renditionId.equals(td.getName())) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    throw new NotFoundException(
                            renditionId + " is not applicable for the node's mimeType " + contentMimeType);
                }
            }
            return toApiRendition(thumbnailDefinition);
        }

        return toApiRendition(renditionNodeRef);
    }

    @Override
    public void createRendition(String nodeId, Rendition rendition, Parameters parameters) {
        // If thumbnail generation has been configured off, then don't bother.
        if (!thumbnailService.getThumbnailsEnabled()) {
            throw new DisabledServiceException("Thumbnail generation has been disabled.");
        }

        final NodeRef sourceNodeRef = validateSourceNode(nodeId);
        final NodeRef renditionNodeRef = getRenditionByName(sourceNodeRef, rendition.getId(), parameters);
        if (renditionNodeRef != null) {
            throw new ConstraintViolatedException(rendition.getId() + " rendition already exists.");
        }

        // Use the thumbnail registry to get the details of the thumbnail
        ThumbnailRegistry registry = thumbnailService.getThumbnailRegistry();
        ThumbnailDefinition thumbnailDefinition = registry.getThumbnailDefinition(rendition.getId());
        if (thumbnailDefinition == null) {
            throw new NotFoundException(rendition.getId() + " is not registered.");
        }

        ContentData contentData = getContentData(sourceNodeRef, true);
        // Check if anything is currently available to generate thumbnails for the specified mimeType
        if (!registry.isThumbnailDefinitionAvailable(contentData.getContentUrl(), contentData.getMimetype(),
                contentData.getSize(), sourceNodeRef, thumbnailDefinition)) {
            throw new InvalidArgumentException("Unable to create thumbnail '" + thumbnailDefinition.getName()
                    + "' for " + contentData.getMimetype() + " as no transformer is currently available.");
        }

        Action action = ThumbnailHelper.createCreateThumbnailAction(thumbnailDefinition, serviceRegistry);
        // Queue async creation of thumbnail
        actionService.executeAction(action, sourceNodeRef, true, true);
    }

    @Override
    public BinaryResource getContent(String nodeId, String renditionId, Parameters parameters) {
        final NodeRef sourceNodeRef = validateSourceNode(nodeId);
        return getContent(sourceNodeRef, renditionId, parameters);
    }

    @Override
    public BinaryResource getContent(NodeRef sourceNodeRef, String renditionId, Parameters parameters) {
        NodeRef renditionNodeRef = getRenditionByName(sourceNodeRef, renditionId, parameters);

        // By default set attachment header (with rendition Id) unless attachment=false
        boolean attach = true;
        String attachment = parameters.getParameter("attachment");
        if (attachment != null) {
            attach = Boolean.valueOf(attachment);
        }
        final String attachFileName = (attach ? renditionId : null);

        if (renditionNodeRef == null) {
            boolean isPlaceholder = Boolean.valueOf(parameters.getParameter("placeholder"));
            if (!isPlaceholder) {
                throw new NotFoundException("Thumbnail was not found for [" + renditionId + ']');
            }
            String sourceNodeMimeType = getMimeType(sourceNodeRef);
            // resource based on the content's mimeType and rendition id
            String phPath = scriptThumbnailService.getMimeAwarePlaceHolderResourcePath(renditionId,
                    sourceNodeMimeType);
            if (phPath == null) {
                // 404 since no thumbnail was found
                throw new NotFoundException(
                        "Thumbnail was not found and no placeholder resource available for [" + renditionId + ']');
            } else {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Retrieving content from resource path [" + phPath + ']');
                }
                // get extension of resource
                String ext = "";
                int extIndex = phPath.lastIndexOf('.');
                if (extIndex != -1) {
                    ext = phPath.substring(extIndex);
                }

                try {
                    final String resourcePath = "classpath:" + phPath;
                    InputStream inputStream = resourceLoader.getResource(resourcePath).getInputStream();
                    // create temporary file
                    File file = TempFileProvider.createTempFile(inputStream, "RenditionsApi-", ext);
                    return new FileBinaryResource(file, attachFileName);
                } catch (Exception ex) {
                    if (LOGGER.isErrorEnabled()) {
                        LOGGER.error("Couldn't load the placeholder." + ex.getMessage());
                    }
                    new ApiException("Couldn't load the placeholder.");
                }
            }
        }

        Map<QName, Serializable> nodeProps = nodeService.getProperties(renditionNodeRef);
        ContentData contentData = (ContentData) nodeProps.get(ContentModel.PROP_CONTENT);
        Date modified = (Date) nodeProps.get(ContentModel.PROP_MODIFIED);

        org.alfresco.rest.framework.resource.content.ContentInfo contentInfo = null;
        if (contentData != null) {
            contentInfo = new ContentInfoImpl(contentData.getMimetype(), contentData.getEncoding(),
                    contentData.getSize(), contentData.getLocale());
        }
        // add cache settings
        CacheDirective cacheDirective = new CacheDirective.Builder().setNeverCache(false).setMustRevalidate(false)
                .setLastModified(modified).setETag(modified != null ? Long.toString(modified.getTime()) : null)
                .setMaxAge(Long.valueOf(31536000))// one year (in seconds)
                .build();

        return new NodeBinaryResource(renditionNodeRef, ContentModel.PROP_CONTENT, contentInfo, attachFileName,
                cacheDirective);
    }

    protected NodeRef getRenditionByName(NodeRef nodeRef, String renditionId, Parameters parameters) {
        if (StringUtils.isEmpty(renditionId)) {
            throw new InvalidArgumentException("renditionId can't be null or empty.");
        }
        // Thumbnails have a cm: prefix.
        QName renditionQName = QName.resolveToQName(namespaceService, renditionId);

        ChildAssociationRef nodeRefRendition = renditionService.getRenditionByName(nodeRef, renditionQName);
        if (nodeRefRendition == null) {
            return null;
        }

        return tenantService.getName(nodeRef, nodeRefRendition.getChildRef());
    }

    protected Rendition toApiRendition(NodeRef renditionNodeRef) {
        Rendition apiRendition = new Rendition();

        String renditionName = (String) nodeService.getProperty(renditionNodeRef, ContentModel.PROP_NAME);
        apiRendition.setId(renditionName);

        ContentData contentData = getContentData(renditionNodeRef, false);
        ContentInfo contentInfo = null;
        if (contentData != null) {
            contentInfo = new ContentInfo(contentData.getMimetype(),
                    getMimeTypeDisplayName(contentData.getMimetype()), contentData.getSize(),
                    contentData.getEncoding());
        }
        apiRendition.setContent(contentInfo);
        apiRendition.setStatus(RenditionStatus.CREATED);

        return apiRendition;
    }

    protected Rendition toApiRendition(ThumbnailDefinition thumbnailDefinition) {
        ContentInfo contentInfo = new ContentInfo(thumbnailDefinition.getMimetype(),
                getMimeTypeDisplayName(thumbnailDefinition.getMimetype()), null, null);
        Rendition apiRendition = new Rendition();
        apiRendition.setId(thumbnailDefinition.getName());
        apiRendition.setContent(contentInfo);
        apiRendition.setStatus(RenditionStatus.NOT_CREATED);

        return apiRendition;
    }

    protected NodeRef validateSourceNode(String nodeId) {
        final NodeRef nodeRef = nodes.validateNode(nodeId);
        if (!nodes.isSubClass(nodeRef, ContentModel.PROP_CONTENT, false)) {
            throw new InvalidArgumentException("Node id '" + nodeId + "' does not represent a file.");
        }
        return nodeRef;
    }

    private String getMimeTypeDisplayName(String mimeType) {
        return mimetypeService.getDisplaysByMimetype().get(mimeType);
    }

    private ContentData getContentData(NodeRef nodeRef, boolean validate) {
        ContentData contentData = (ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
        if (validate && !ContentData.hasContent(contentData)) {
            throw new InvalidArgumentException("Node id '" + nodeRef.getId() + "' has no content.");
        }
        return contentData;
    }

    private String getMimeType(NodeRef nodeRef) {
        ContentData contentData = getContentData(nodeRef, true);
        return contentData.getMimetype();
    }
}