All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.aem.formsndocuments.publish.AssetReferenceProvider Maven / Gradle / Ivy

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2014 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/
package com.adobe.aem.formsndocuments.publish;

import java.util.HashSet;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import com.adobe.aemforms.fm.exception.FormsMgrException;
import com.day.cq.replication.PathNotFoundException;
import com.day.cq.search.Predicate;
import com.day.cq.search.PredicateGroup;
import com.day.cq.search.Query;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.eval.JcrPropertyPredicateEvaluator;
import com.day.cq.search.eval.PathPredicateEvaluator;
import com.day.cq.search.eval.TypePredicateEvaluator;
import com.day.cq.search.result.SearchResult;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.jcr.api.SlingRepository;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.adobe.aem.formsndocuments.service.FormsRelationService;
import com.adobe.aem.formsndocuments.transferobjects.AssetInfo;
import com.adobe.aem.formsndocuments.util.FMConstants;
import com.adobe.aem.formsndocuments.util.FMUtils;
import com.adobe.granite.resourceresolverhelper.ResourceResolverHelper;
import com.day.cq.commons.jcr.JcrConstants;
import com.day.cq.wcm.api.reference.Reference;
import com.day.cq.wcm.api.reference.ReferenceProvider;

/**
 * The AssetReferenceProvider finds references for FM Assets.
 * 
 */
@Component
@Service(ReferenceProvider.class)
public class AssetReferenceProvider implements ReferenceProvider {

    /** Default log. */
    private final Logger log = LoggerFactory
            .getLogger(AssetReferenceProvider.class);

    private BundleContext bundleContext;

    @org.apache.felix.scr.annotations.Reference(referenceInterface=ResourceResolverHelper.class)
    ResourceResolverHelper resourceResolverHelper;

    @org.apache.felix.scr.annotations.Reference
    private SlingRepository repository = null;

	@org.apache.felix.scr.annotations.Reference(referenceInterface = QueryBuilder.class)
	private QueryBuilder queryBuilder;

	protected void activate(ComponentContext componentContext) throws Exception {
		bundleContext = componentContext.getBundleContext();
	}


    public List findReferences(Resource resource) {

        List references = new ArrayList();

        Session session = null; 
        boolean localSession = false;
        try {
            session = resourceResolverHelper.getResourceResolverAs(Session.class);
            if (session == null) {
                // Session could be null in case of invocation from ReplicationScheduler.
                // In this case acquire 'fd-service' system user.
                session = FMUtils.getFnDServiceUserSession(repository);
                localSession = true;
            }

			/* fetching relation service here and not by @Reference annotation. As FormsRelationService uses AssetReferenceProvider to find all child assets,
				this creates cyclic dependency. Although, we were managing this situation by marking reference as optional and unary but
				Evergreen (cq auto pull requests merge routine) tests failing due to this.
				JIRA - https://jira.corp.adobe.com/browse/CQ-106888
			  */
			ServiceReference serviceReference         = bundleContext.getServiceReference(FormsRelationService.class.getName());
			FormsRelationService formsRelationService = (FormsRelationService) bundleContext.getService(serviceReference);
			ResourceResolver resourceResolver         = resource.getResourceResolver();
			Set allRelatedChildAssets 	  = new HashSet();
			List nodePaths                    = resolvePathsToFindReferences(resource.getPath(), session);

			for (String nodePath : nodePaths) {
				formsRelationService.getAllRelatedChildAssets(resourceResolver, nodePath, allRelatedChildAssets);
				for (AssetInfo formInfo: allRelatedChildAssets) {
					references.add(new Reference(getPublishType(formInfo), formInfo.getResource().getName(), formInfo.getResource(), getLastModifiedTime(session, formInfo)));
				}
			}
		} catch (Exception e) {
			// In case of exception, we log the exception and return empty list.
			log.error("Failed to retrieve asset dependencies. Cause:"+e.getMessage(), e);
		}
		finally {
		    // If session acquired locally, close it.
            if (localSession) {
                if (session != null) {
                    session.logout();
                }
            }
        }

        return references;
    }

	List resolvePathsToFindReferences(String path, Session session) throws PathNotFoundException, RepositoryException {
		List paths = new ArrayList();

		// ------------------- check if this is FM Asset. If yes, return dam:asset path for this -----------------------
		if(path.startsWith(FMConstants.AF_ROOT)){
			path = FMUtils.getAssetPathFromPage(toResourcePath(path));
		}

		if(path.startsWith(FMConstants.SHADOW_NODES_ROOT)) {
			Node rootAssetNode = session.getNode(toResourcePath(path));
			if (rootAssetNode.isNodeType(FMConstants.DAM_ASSET_NODETYPE)) {
				paths.add(path);
			}

			return paths;
		}

		// ------------------- check if this is cq page for sites. If yes, find if there is any Guide/AD referred in it -----------------------
		Node node = session.getNode(toResourcePath(path));

		if( node.isNodeType(FMConstants.CQ_PAGE_NODETYPE) ) {
			log.debug("Entering query for :  resolvePathsToFindReferences for asset : " + path);

			PredicateGroup predicateGroup = new PredicateGroup();
			predicateGroup.setAllRequired(true);

			Predicate typePredicate = new Predicate(TypePredicateEvaluator.TYPE);
			typePredicate.set(TypePredicateEvaluator.TYPE, JcrConstants.NT_UNSTRUCTURED);

			Predicate pathPredicate = new Predicate(PathPredicateEvaluator.PATH);
			pathPredicate.set(PathPredicateEvaluator.PATH, path);
			pathPredicate.set(PathPredicateEvaluator.EXACT, "false");

			PredicateGroup refPredicateGroup = new PredicateGroup();

			// Find Adaptive Form/Adaptive Document being referred in this page
			// AEM page(sites) contains a node with "sling:resourceType" property set to value "fd/af/components/aemform".
			// AF/AD are stored in property "formRef" path pointing to dam asset node
			Predicate refPredicate = new Predicate(JcrPropertyPredicateEvaluator.PROPERTY);
			refPredicate.set(JcrPropertyPredicateEvaluator.PROPERTY, FMConstants.SLING_RES_TYPE);
			refPredicate.set(JcrPropertyPredicateEvaluator.OPERATION, JcrPropertyPredicateEvaluator.OP_EQUALS);
			refPredicate.set(JcrPropertyPredicateEvaluator.VALUE, FMConstants.AEM_FORM_RESOURCE_TYPE);

			refPredicateGroup.add(refPredicate);

			predicateGroup.add(refPredicateGroup);
			predicateGroup.add(typePredicate);
			predicateGroup.add(pathPredicate);

			Query query = queryBuilder.createQuery(predicateGroup, session);
			SearchResult result = query.getResult();

			log.debug("Query execution time (ms) " + result.getExecutionTimeMillis());

			if (result != null) {
				Iterator iter = result.getNodes();
				while(iter.hasNext()) {
					Node assetNode = iter.next();
					checkPropertyAndAddPath(assetNode, FMConstants.PROPERTYNAME_FORM_REF, paths);
				}
			}
		}

		return paths;
	}

	private void checkPropertyAndAddPath(Node assetNode, String propertyName, List paths) throws RepositoryException{
		if(assetNode.hasProperty(propertyName)) {
			String assetPath = assetNode.getProperty(propertyName).getString();
			if(assetPath.startsWith(FMConstants.AF_ROOT)) {
				// although sites page contains dam:asset path of the asset, this is just to ensure we get asset path if there is any change by sites and they start keeping AF page path.
				assetPath = FMUtils.getAssetPathFromPage(assetPath);
			}
			paths.add(assetPath);
		}
	}

	private String getPublishType(AssetInfo formInfo) throws RepositoryException {
		/* To show list of assets in cq publish wizard (used by sites publish and publish from AF Authoring), we are returning asset type as either "asset" (if it is dam asset) or "form" otherwise.
		   publish wizard maps asset type to an header under which it shows list of assets. All dam:asset nodes are mapped under "asset". We don't want to change it because all direct dam asset references are read by dam reference provider and are marked as "asset". So we are using the same.
		   rest all nodes to be published are sent as "form" asset type that gets mapped to "All Form Resources" in UI.
		   For any change to be made here, sanity for CQ-77255 should be done.
		 */

		String publishType = "form";
		Resource res = formInfo.getResource();
		if(res != null ) {
			Node node = res.adaptTo(Node.class);
			if (node != null && node.isNodeType(FMConstants.DAM_ASSET_NODETYPE)) {
				publishType = "asset";
			}
		}
		return publishType;
	}

	/**
	 * This function retrieves the last modification time of the provided asset.
	 * If this value is -1, it implies that we were unable to find the last modified time of the concerned asset, in which case Long.MAX_VALUE is returned.
	 * @param session
	 * @param assetInfo
	 * @return
	 * @throws FormsMgrException
	 */
	private long getLastModifiedTime(Session session, AssetInfo assetInfo) throws FormsMgrException{
		/* Following code has been added to ensure that assets that do not have jcr:lastModified property are always published. For instance : content policies & theme clientlib.
           The assetInfo returns -1 as the LMT for such assets but since the value -1 is always less than the replication time,
           these assets are never published. It is better to rather publish them every time than not publishing them even after they are modified.
           However, this is not the best solution and this function should be revisited and restructured such that it is able to gracefully handle cache invalidation as well as publish.
		 */
		long lastModifiedTime = assetInfo.getLastModifiedOrCreatedTime(session);
		if (lastModifiedTime == -1L) {
			lastModifiedTime = Long.MAX_VALUE;
		}
		return lastModifiedTime;
	}
	
	private String toResourcePath(String path) {
		if (path.endsWith("/" + JcrConstants.JCR_CONTENT)) {
			path = path.substring(0, path.length() - JcrConstants.JCR_CONTENT.length() - 1);
		}
		return path;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy