com.adobe.acs.commons.mcp.impl.processes.AssetIngestor.java Source code

Java tutorial

Introduction

Here is the source code for com.adobe.acs.commons.mcp.impl.processes.AssetIngestor.java

Source

/*
 * #%L
 * ACS AEM Commons Bundle
 * %%
 * Copyright (C) 2017 Adobe
 * %%
 * 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.
 * #L%
 */
package com.adobe.acs.commons.mcp.impl.processes;

import com.adobe.acs.commons.fam.ActionManager;
import com.adobe.acs.commons.functions.CheckedConsumer;
import com.adobe.acs.commons.mcp.ProcessDefinition;
import com.adobe.acs.commons.mcp.ProcessInstance;
import com.adobe.acs.commons.mcp.form.FormField;
import com.adobe.acs.commons.mcp.form.PathfieldComponent;
import com.adobe.acs.commons.mcp.form.RadioComponent;
import com.adobe.acs.commons.mcp.model.FieldFormat;
import com.adobe.acs.commons.mcp.model.GenericReport;
import com.adobe.acs.commons.mcp.model.ValueFormat;
import com.day.cq.commons.jcr.JcrConstants;
import com.day.cq.commons.jcr.JcrUtil;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.AssetManager;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.commons.mime.MimeTypeService;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

public abstract class AssetIngestor extends ProcessDefinition {
    private final MimeTypeService mimetypeService;

    @SuppressWarnings("squid:S00115")
    public enum AssetAction {
        skip, version, replace
    }

    public AssetIngestor(MimeTypeService mimeTypeService) {
        this.mimetypeService = mimeTypeService;
    }

    @FormField(name = "Target JCR Folder", description = "Base folder for ingestion", hint = "/content/dam", component = PathfieldComponent.FolderSelectComponent.class, required = true, options = {
            "default=/content/dam", "base=/content/dam" })
    String jcrBasePath;
    @FormField(name = "Ignore folders", description = "List of folder names to be ignored", hint = "tmp,.DS_STORE", options = {
            "default=tmp,ds_store,.ds_store,.thumbs,.appledouble" })
    String ignoreFolders;
    List<String> ignoreFolderList;

    @FormField(name = "Ignore files", description = "List of file names to ignore", hint = "full file names, comma separated", options = {
            "default=ds_store,.ds_store" })
    String ignoreFiles;
    List<String> ignoreFileList;

    @FormField(name = "Ignore extensions", description = "List of file extensions to ignore", hint = "mp4,txt, etc.", options = {
            "default=txt,html,css,js,thm,exe,db" })
    String ignoreExtensions;
    List<String> ignoreExtensionList;

    @FormField(name = "Existing action", description = "What to do if an asset exists", component = RadioComponent.EnumerationSelector.class, options = {
            "default=skip", "vertical" })
    AssetAction existingAssetAction;
    @FormField(name = "Minimum size", description = "Min size to import (in bytes), 0=none", hint = "1024...", options = {
            "default=1024" })
    long minimumSize;
    @FormField(name = "Maximum size", description = "Max size to import (in bytes), 0=none", hint = "1gb = 1073741824", options = {
            "default=1073741824" })
    long maximumSize;

    protected static final String DEFAULT_FOLDER_TYPE = "sling:Folder";
    protected static final String CHANGED_BY_WORKFLOW = "changedByWorkflowProcess";

    AtomicInteger folderCount = new AtomicInteger();
    AtomicInteger assetCount = new AtomicInteger();
    AtomicInteger filesSkipped = new AtomicInteger();
    AtomicLong totalImportedData = new AtomicLong();

    @Override
    public void init() throws RepositoryException {
        if (ignoreFolders == null) {
            ignoreFolders = "";
        }
        ignoreFolderList = Arrays.asList(ignoreFolders.trim().toLowerCase().split(","));
        if (ignoreFiles == null) {
            ignoreFiles = "";
        }
        ignoreFileList = Arrays.asList(ignoreFiles.trim().toLowerCase().split(","));
        if (ignoreExtensions == null) {
            ignoreExtensions = "";
        }
        ignoreExtensionList = Arrays.asList(ignoreExtensions.trim().toLowerCase().split(","));
    }

    @SuppressWarnings("squid:S00112")
    private void createAsset(Source source, String assetPath, ResourceResolver r, boolean versioning)
            throws Exception {
        r.adaptTo(Session.class).getWorkspace().getObservationManager().setUserData(CHANGED_BY_WORKFLOW);
        AssetManager assetManager = r.adaptTo(AssetManager.class);
        String type = mimetypeService.getMimeType(source.getName());
        if (versioning) {
            //is asset is null, no version gets created
            Asset asset = r.getResource(assetPath).adaptTo(Asset.class);
            //once you are past this first version, default behavior is to start numbering 1.0, 1.1 and so on
            assetManager.createRevision(asset, "initial version of asset", asset.getName());
            r.commit();
            r.refresh();
            //once version is committed we are safe to create, which only replaces the original version
        }
        assetManager.createAsset(assetPath, source.getStream(), type, false);
        r.commit();
        r.refresh();
        totalImportedData.accumulateAndGet(source.getLength(), (p, x) -> p + x);
        assetCount.incrementAndGet();
    }

    protected void handleExistingAsset(Source source, String assetPath, ResourceResolver r) throws Exception {
        switch (existingAssetAction) {
        case skip:
            //if skip then we only create asset if it doesn't exist
            if (r.getResource(assetPath) == null) {
                createAsset(source, assetPath, r, false);
            } else {
                filesSkipped.incrementAndGet();
            }
            break;
        case replace:
            //if replace we just create a new one and the old one goes away
            createAsset(source, assetPath, r, false);
            break;
        default:
            //only option left is replace, we'll save current version as a version and then replace it
            versionExistingAsset(source, assetPath, r);
        }
    }

    protected boolean createFolderNode(HierarchialElement el, ResourceResolver r)
            throws RepositoryException, PersistenceException {
        if (el == null || !el.isFolder()) {
            return false;
        }
        String folderPath = el.getNodePath();
        String name = el.getName();
        Session s = r.adaptTo(Session.class);
        if (s.nodeExists(folderPath)) {
            Node folderNode = s.getNode(folderPath);
            if (folderPath.equals(jcrBasePath) || (folderNode.hasProperty(JcrConstants.JCR_TITLE)
                    && folderNode.getProperty(JcrConstants.JCR_TITLE).getString().equals(name))) {
                return false;
            } else {
                folderNode.setProperty(JcrConstants.JCR_TITLE, name);
                r.commit();
                r.refresh();
                return true;
            }
        }
        HierarchialElement parent = el.getParent();
        String parentPath;
        if (parent == null) {
            parentPath = jcrBasePath;
        } else {
            parentPath = parent.getNodePath();
        }
        if (!jcrBasePath.equals(parentPath)) {
            createFolderNode(parent, r);
        }
        Node child = s.getNode(parentPath).addNode(el.getNodeName(), DEFAULT_FOLDER_TYPE);
        folderCount.incrementAndGet();
        if (!folderPath.equals(jcrBasePath)) {
            child.setProperty(JcrConstants.JCR_TITLE, name);
        }
        r.commit();
        r.refresh();
        return true;
    }

    @SuppressWarnings("squid:S00112")
    private void versionExistingAsset(Source source, String assetPath, ResourceResolver r) throws Exception {
        createAsset(source, assetPath, r, r.getResource(assetPath) != null);
    }

    protected CheckedConsumer<ResourceResolver> importAsset(final Source source, ActionManager actionManager) {
        return (ResourceResolver r) -> {
            String path = source.getElement().getNodePath();
            createFolderNode(source.getElement().getParent(), r);
            actionManager.setCurrentItem(source.getElement().getItemName());
            handleExistingAsset(source, path, r);
        };
    }

    protected boolean canImportFile(Source source) {
        String name = source.getName().toLowerCase();
        if (minimumSize > 0 && source.getLength() < minimumSize) {
            return false;
        }
        if (maximumSize > 0 && source.getLength() > maximumSize) {
            return false;
        }
        if (name.startsWith(".") || ignoreFileList.contains(name)) {
            return false;
        }
        if (name.contains(".")) {
            int extPos = name.lastIndexOf('.');
            String ext = name.substring(extPos + 1);
            if (ignoreExtensionList.contains(ext)) {
                return false;
            }
        }
        return true;
    }

    protected boolean canImportFolder(HierarchialElement element) {
        String name = element.getName();
        if (ignoreFolderList.contains(name.toLowerCase())) {
            return false;
        } else {
            HierarchialElement parent = element.getParent();
            if (parent == null) {
                return true;
            } else {
                return canImportFolder(parent);
            }
        }
    }

    protected boolean canImportContainingFolder(HierarchialElement element) {
        HierarchialElement parent = element.getParent();
        if (parent == null) {
            return true;
        } else {
            return canImportFolder(parent);
        }
    }

    @SuppressWarnings("squid:S00115")
    enum ReportColumns {
        folder_count, asset_count, files_skipped, @FieldFormat(ValueFormat.storageSize)
        data_imported
    }

    GenericReport report = new GenericReport();

    @Override
    public void storeReport(ProcessInstance instance, ResourceResolver rr)
            throws RepositoryException, PersistenceException {
        EnumMap<ReportColumns, Object> values = new EnumMap<>(ReportColumns.class);
        List<EnumMap<ReportColumns, Object>> rows = new ArrayList<>();
        rows.add(values);
        values.put(ReportColumns.folder_count, folderCount);
        values.put(ReportColumns.asset_count, assetCount);
        values.put(ReportColumns.files_skipped, filesSkipped);
        values.put(ReportColumns.data_imported, totalImportedData);
        report.setRows(rows, ReportColumns.class);
        report.persist(rr, instance.getPath() + "/jcr:content/report");
    }

    protected interface Source {

        String getName();

        InputStream getStream() throws IOException;

        long getLength();

        HierarchialElement getElement();

    }

    protected interface HierarchialElement {

        boolean isFile();

        boolean isFolder();

        HierarchialElement getParent();

        String getName();

        String getItemName();

        Source getSource();

        String getJcrBasePath();

        default String getNodePath() {
            HierarchialElement parent = getParent();
            return (parent == null ? getJcrBasePath() : parent.getNodePath()) + "/" + getNodeName();
        }

        default String getNodeName() {
            String name = getName();
            if (isFile() && name.contains(".")) {
                String baseName = StringUtils.substringBeforeLast(name, ".");
                String extension = StringUtils.substringAfterLast(name, ".");
                return JcrUtil.createValidName(baseName) + "." + JcrUtil.createValidName(extension);
            } else {
                return JcrUtil.createValidName(name);
            }
        }
    }
}