Package com.adobe.acs.commons.replication.status.impl

Source Code of com.adobe.acs.commons.replication.status.impl.JcrPackageReplicationStatusEventHandler

/*
* #%L
* ACS AEM Commons Bundle
* %%
* Copyright (C) 2014 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.replication.status.impl;

import com.adobe.acs.commons.packaging.PackageHelper;
import com.adobe.acs.commons.replication.status.ReplicationStatusManager;
import com.day.cq.jcrclustersupport.ClusterAware;
import com.day.cq.replication.ReplicationAction;
import com.day.cq.replication.ReplicationStatus;
import com.day.jcr.vault.packaging.JcrPackage;
import com.day.jcr.vault.packaging.PackageException;
import com.day.jcr.vault.packaging.Packaging;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.ConfigurationPolicy;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.PropertyOption;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.JcrConstants;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.event.jobs.JobProcessor;
import org.apache.sling.event.jobs.JobUtil;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

@Component(
        label = "ACS AEM Commons - Package Replication Status Updater",
        description = "Event handler that listens for Jcr Package replications and updates the Replication Status of "
                 + "its content accordingly.",
        metatype = true,
        immediate =  true,
        policy = ConfigurationPolicy.REQUIRE
)
@Properties({
        @Property(
                label = "Event Topics",
                value = { ReplicationAction.EVENT_TOPIC },
                description = "[Required] Event Topics this event handler will to respond to.",
                name = EventConstants.EVENT_TOPIC,
                propertyPrivate = true
        ),
        @Property(
                label = "Event Filters",
                value = "(" + ReplicationAction.PROPERTY_TYPE + "=ACTIVATE)",
                name = EventConstants.EVENT_FILTER,
                propertyPrivate = true
        )
})
@Service
public class JcrPackageReplicationStatusEventHandler implements JobProcessor, EventHandler, ClusterAware {
    private static final Logger log = LoggerFactory.getLogger(ReplicationStatusManagerImpl.class);

    private enum ReplicatedAt {
        CURRENT_TIME,
        PACKAGE_LAST_MODIFIED;
    }

    private static final String[] DEFAULT_REPLICATION_STATUS_NODE_TYPES = {
            ReplicationStatus.NODE_TYPE,
            "cq:PageContent",
            "dam:AssetContent",
            "rep:User",
            "rep:Group"
    };

    private String[] replicationStatusNodeTypes = DEFAULT_REPLICATION_STATUS_NODE_TYPES;

    @Property(label = "Replication Status Types",
            description = "Node types that are candidates to update Replication Status on",
            cardinality = Integer.MAX_VALUE,
            value = {
                    ReplicationStatus.NODE_TYPE,
                    "cq:PageContent",
                    "dam:AssetContent",
                    "rep:User",
                    "rep:Group"
            })
    public static final String PROP_REPLICATION_STATUS_NODE_TYPES = "node-types";

    @Reference
    private Packaging packaging;

    @Reference
    private ResourceResolverFactory resourceResolverFactory;

    @Reference
    private ReplicationStatusManager replicationStatusManager;

    @Reference
    private PackageHelper packageHelper;

    private ResourceResolver adminResourceResolver;

    private boolean isMaster = false;

    private static final String DEFAULT_REPLICATED_BY = "Package Replication";
    private String replicatedBy = DEFAULT_REPLICATED_BY;
    @Property(label = "Replicated By",
            description = "The 'name' to set the 'replicated by' property to. Defaults to: " + DEFAULT_REPLICATED_BY,
            value = DEFAULT_REPLICATED_BY)
    public static final String PROP_REPLICATED_BY = "replicated-by";

    private static final ReplicatedAt DEFAULT_REPLICATED_AT = ReplicatedAt.PACKAGE_LAST_MODIFIED;
    private ReplicatedAt replicatedAt = DEFAULT_REPLICATED_AT;
    @Property(label = "Replicated At",
            description = "The 'value' used to set the 'replicated at' property. [ Default: Package Last Modified ]",
            options = {
                    @PropertyOption(
                        name = "PACKAGE_LAST_MODIFIED",
                        value = "Package Last Modified"
                    ),
                    @PropertyOption(
                        name = "CURRENT_TIME",
                        value = "Current Time"
                    )
            })
    public static final String PROP_REPLICATED_AT = "replicated-at";

    @Override
    public final void handleEvent(final Event event) {
        if (this.isMaster) {
            // Only run on master

            final String[] paths = (String[]) event.getProperty("paths");

            if (this.containsJcrPackagePath(paths) && !CollectionUtils.isEmpty(this.getJcrPackages(paths))) {
                JobUtil.processJob(event, this);
            }
        }
    }

    @Override
    public final boolean process(final Event event) {
        final String[] paths = (String[]) event.getProperty("paths");

        log.debug("Processing Replication Status Update for JCR Package: {}", paths);

        final List<JcrPackage> jcrPackages = this.getJcrPackages(paths);

        if (CollectionUtils.isEmpty(jcrPackages)) {
            log.warn("JCR Package is unavailable for Replication Status Update at: {}", paths);
            return true;
        }

        for (final JcrPackage jcrPackage : jcrPackages) {
            try {
                final List<Resource> resources = new ArrayList<Resource>();

                for (final String packagePath : packageHelper.getContents(jcrPackage)) {
                    final Resource resource = this.adminResourceResolver.getResource(packagePath);
                    if (this.accept(resource))  {
                        resources.add(resource);
                    }
                }

                if (resources.size() > 0) {
                    replicationStatusManager.setReplicationStatus(this.adminResourceResolver,
                            this.replicatedBy,
                            getJcrPackageLastModified(this.adminResourceResolver, jcrPackage),
                            ReplicationStatusManager.Status.ACTIVATED,
                            resources.toArray(new Resource[resources.size()]));

                    log.info("Updated Replication Status for JCR Package: {}", jcrPackage.getDefinition().getId());
                } else {
                    log.info("Could not find any resources in JCR Package [ {} ] that are candidates to have their Replication Status updated",
                            jcrPackage.getDefinition().getId());
                }
            } catch (RepositoryException e) {
                log.error("RepositoryException occurred updating replication status for contents of package");
                log.error(e.getMessage());

            } catch (IOException e) {
                log.error("IOException occurred updating replication status for contents of package");
                log.error(e.getMessage());

            } catch (PackageException e) {
                log.error("Could not retrieve the Packages contents.");
                log.error(e.getMessage());
            }
        }

        return true;
    }

    /**
     * Checks if any path in the array of paths looks like a Jcr Package path.
     *
     * Provides a very fast, String-based, in-memory check to weed out most false positives and avoid
     * resolving the path to a Jcr Package and ensure it is valid.
     *
     * @param paths the array of paths
     * @return true if at least one path looks like a Jcr Package path
     */
    private boolean containsJcrPackagePath(final String[] paths) {
        for (final String path : paths) {
            if (StringUtils.startsWith(path, "/etc/packages/")
                    && StringUtils.endsWith(path, ".zip")) {
                // At least 1 entry looks like a package
                return true;
            }
        }

        // Nothing looks like a package...
        return false;
    }

    /**
     * Resolves paths to Jcr Packages. If any path does not resolve to a valid Jcr Package, it is discarded.
     *
     * @param paths the list of paths to resolve to Jcr Packages
     * @return a list of Jcr Packages that correspond to the provided paths
     */
    private List<JcrPackage> getJcrPackages(final String[] paths) {
        final List<JcrPackage> packages = new ArrayList<JcrPackage>();

        for (final String path : paths) {
            final Resource eventResource = this.adminResourceResolver.getResource(path);

            JcrPackage jcrPackage;

            try {
                jcrPackage = packaging.open(eventResource.adaptTo(Node.class), false);
                if (jcrPackage != null) {
                    packages.add(jcrPackage);
                }
            } catch (RepositoryException e) {
                log.warn("Error checking if the path [ {} ] is a JCR Package.", path);
            }

        }
        return packages;
    }

    /**
     * Checks if the ReplicationStatusManager should make the provides resource w replication status.
     *
     * @param resource the return
     * @return true is the resource is markable resource
     * @throws RepositoryException
     */
    private boolean accept(final Resource resource) throws RepositoryException {
        if (resource != null && !ResourceUtil.isNonExistingResource(resource)) {
            final Node node = resource.adaptTo(Node.class);

            if (node != null) {
                for (final String nodeType : this.replicationStatusNodeTypes) {
                    if (node.isNodeType(nodeType)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Gets the last build time of the package.
     *
     * @param resourceResolver the resource resolver to access the package properties
     * @param jcrPackage the package obj
     * @return the package's last build time or null if none can be found
     * @throws RepositoryException
     */
    private Calendar getJcrPackageLastModified(final ResourceResolver resourceResolver,
                                               final JcrPackage jcrPackage) throws RepositoryException {
        if (ReplicatedAt.CURRENT_TIME.equals(this.replicatedAt)) {
            return Calendar.getInstance();
        } else {
            final String path = jcrPackage.getNode().getPath();
            final Resource resource = resourceResolver.getResource(path).getChild(JcrConstants.JCR_CONTENT);
            final ValueMap properties = resource.adaptTo(ValueMap.class);

            return properties.get(JcrConstants.JCR_LASTMODIFIED, Calendar.class);
        }
    }

    @Activate
    private void activate(final Map<String, String> config) throws LoginException {
        log.trace("Activating the ACS AEM Commons - JCR Package Replication Status Updater (Event Handler)");

        this.adminResourceResolver = resourceResolverFactory.getAdministrativeResourceResolver(null);

        this.replicatedBy = PropertiesUtil.toString(config.get(PROP_REPLICATED_BY), DEFAULT_REPLICATED_BY);

        String tmp = PropertiesUtil.toString(config.get(PROP_REPLICATED_AT), "");
        try {
            this.replicatedAt = ReplicatedAt.valueOf(tmp);
        } catch (IllegalArgumentException ex) {
            this.replicatedAt = ReplicatedAt.PACKAGE_LAST_MODIFIED;
        }

        this.replicationStatusNodeTypes = PropertiesUtil.toStringArray(config.get(PROP_REPLICATION_STATUS_NODE_TYPES),
                DEFAULT_REPLICATION_STATUS_NODE_TYPES);

        log.info("Package Replication Status - Replicated By: [ {} ]", this.replicatedBy);
        log.info("Package Replication Status - Replicated At: [ {} ]", this.replicatedAt.toString());
        log.info("Package Replication Status - Node Types: [ {} ]",
                StringUtils.join(this.replicationStatusNodeTypes, ", "));
    }

    @Deactivate
    private void deactivate(final Map<String, String> properties) {
        if (this.adminResourceResolver != null) {
            this.adminResourceResolver.close();
        }
    }

    @Override
    public final void bindRepository(String repositoryId, String clusterId, boolean newIsMaster) {
        this.isMaster = newIsMaster;
    }

    @Override
    public final void unbindRepository() {
        this.isMaster = false;
    }
}
TOP

Related Classes of com.adobe.acs.commons.replication.status.impl.JcrPackageReplicationStatusEventHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.