ReplicateAssetOnModify.java 14.3 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  com.day.cq.replication.Agent
 *  com.day.cq.replication.AgentConfig
 *  com.day.cq.replication.AgentFilter
 *  com.day.cq.replication.ReplicationAction
 *  com.day.cq.replication.ReplicationActionType
 *  com.day.cq.replication.ReplicationException
 *  com.day.cq.replication.ReplicationOptions
 *  com.day.cq.replication.Replicator
 *  com.scene7.is.util.collections.CollectionUtil
 *  javax.jcr.RepositoryException
 *  javax.jcr.Session
 *  javax.jcr.Workspace
 *  javax.jcr.observation.Event
 *  javax.jcr.observation.EventIterator
 *  javax.jcr.observation.EventListener
 *  javax.jcr.observation.ObservationManager
 *  org.apache.felix.scr.annotations.Activate
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Deactivate
 *  org.apache.felix.scr.annotations.Properties
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.jackrabbit.api.observation.JackrabbitEvent
 *  org.apache.sling.commons.osgi.OsgiUtil
 *  org.apache.sling.event.jobs.Job
 *  org.apache.sling.event.jobs.JobManager
 *  org.apache.sling.event.jobs.consumer.JobConsumer
 *  org.apache.sling.event.jobs.consumer.JobConsumer$JobResult
 *  org.apache.sling.jcr.api.SlingRepository
 *  org.jetbrains.annotations.NotNull
 *  org.osgi.service.component.ComponentContext
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.adobe.cq.dam.aod.replication;

import com.day.cq.replication.Agent;
import com.day.cq.replication.AgentConfig;
import com.day.cq.replication.AgentFilter;
import com.day.cq.replication.ReplicationAction;
import com.day.cq.replication.ReplicationActionType;
import com.day.cq.replication.ReplicationException;
import com.day.cq.replication.ReplicationOptions;
import com.day.cq.replication.Replicator;
import com.scene7.is.util.collections.CollectionUtil;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Workspace;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
import javax.jcr.observation.ObservationManager;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
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.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.api.observation.JackrabbitEvent;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.apache.sling.event.jobs.Job;
import org.apache.sling.event.jobs.JobManager;
import org.apache.sling.event.jobs.consumer.JobConsumer;
import org.apache.sling.jcr.api.SlingRepository;
import org.jetbrains.annotations.NotNull;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(metatype=1, immediate=1, label="Adobe CQ Dynamic Media Replicate On Modify", description="Replicates modified assets through replication agents that have the On Modification trigger enabled")
@Service(value={JobConsumer.class})
@Properties(value={@Property(name="job.topics", value={"com/adobe/cq/dam/dmassetreplicateonmodify"}, propertyPrivate=1)})
public class ReplicateAssetOnModify
implements JobConsumer {
    private static final Logger LOGGER = LoggerFactory.getLogger(ReplicateAssetOnModify.class);
    private static final String[] LISTENER_PATHS = new String[]{"/content/dam", "/etc/dam"};
    private static final String SUB_SERVICE_NAME = "dmreplicateonmodify";
    private static final Pattern JCR_CONTENT = Pattern.compile("/jcr:content(/.*)?$");
    private static final String LAST_INVALIDATE_CACHE = "dam:lastInvalidateCache";
    private static final Set<String> IGNORED_PROPS = CollectionUtil.setOf((Object[])new String[]{"cq:lastPublished", "cq:lastPublishedBy", "cq:lastReplicated", "cq:lastReplicatedBy", "cq:lastReplicationAction", "dam:lastInvalidateCache"});
    public static final String JOB_TOPIC = "com/adobe/cq/dam/dmassetreplicateonmodify";
    public static final String JOB_PROP_LIST = "replication.list";
    @Property(boolValue={0}, label="Enabled", description="Automatically replicate assets on modification")
    public static final String PROP_ENABLED = "dmreplicateonmodify.enabled";
    @Reference
    private JobManager jobManager = null;
    @Reference
    private SlingRepository repository = null;
    @Reference
    private Replicator replicator = null;
    private Session observationSession;
    private ObservationManager observationManager;
    private EventListener[] listeners;
    private static final AgentFilter MODIFY_FILTER = new AgentFilter(){

        public boolean isIncluded(Agent agent) {
            return agent.getConfiguration().isTriggeredOnModification();
        }
    };

    @Activate
    protected void activate(ComponentContext ctx) {
        try {
            Dictionary props = ctx.getProperties();
            boolean enabled = OsgiUtil.toBoolean(props.get("dmreplicateonmodify.enabled"), (boolean)true);
            if (enabled) {
                LOGGER.info("Activate Dynamic Media Replicate On Modify");
                this.observationSession = this.repository.loginService("dmreplicateonmodify", null);
                this.observationManager = this.observationSession.getWorkspace().getObservationManager();
                this.listeners = this.registerEventListeners(LISTENER_PATHS);
            }
        }
        catch (RepositoryException e) {
            LOGGER.error("Unable to activate Dynamic Media replicate on modify", (Throwable)e);
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @Deactivate
    protected void deactivate() {
        try {
            if (this.observationManager != null) {
                LOGGER.info("Deactivate Dynamic Media Replicate On Modify");
                this.unregisterEventListeners(this.listeners);
            }
        }
        catch (RepositoryException e) {
            LOGGER.error("Error removing event listener", (Throwable)e);
        }
        finally {
            if (this.observationSession != null) {
                this.observationSession.logout();
            }
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     * Enabled aggressive block sorting
     * Enabled unnecessary exception pruning
     * Enabled aggressive exception aggregation
     */
    public JobConsumer.JobResult process(Job job) {
        try {
            Session session = null;
            try {
                session = this.repository.loginService("dmreplicateonmodify", null);
                List actions = (List)job.getProperty("replication.list");
                for (ReplicationAction action : actions) {
                    try {
                        LOGGER.debug("Replicate {} {}", (Object)action.getType(), (Object)action.getPath());
                        ReplicationOptions options = new ReplicationOptions();
                        options.setFilter(MODIFY_FILTER);
                        this.replicator.replicate(session, action.getType(), action.getPath(), options);
                        continue;
                    }
                    catch (ReplicationException e) {
                        LOGGER.error("Failed to replicate " + action.getPath(), (Throwable)e);
                        JobConsumer.JobResult jobResult = JobConsumer.JobResult.FAILED;
                        if (session == null) return jobResult;
                        {
                            session.logout();
                        }
                        return jobResult;
                    }
                }
                return JobConsumer.JobResult.OK;
            }
            finally {
                if (session != null) {
                    session.logout();
                }
            }
        }
        catch (RepositoryException e) {
            LOGGER.error("Failed to login as a service", (Throwable)e);
            return JobConsumer.JobResult.FAILED;
        }
    }

    @NotNull
    private EventListener[] registerEventListeners(String[] listenerPaths) throws RepositoryException {
        EventListener[] listeners = new ReplicateOnModifyListener[listenerPaths.length];
        for (int i = 0; i != listenerPaths.length; ++i) {
            listeners[i] = new ReplicateOnModifyListener(this.jobManager);
            this.observationManager.addEventListener(listeners[i], 31, listenerPaths[i], true, null, null, true);
        }
        return listeners;
    }

    private void unregisterEventListeners(EventListener[] listeners) throws RepositoryException {
        for (EventListener listener : listeners) {
            this.observationManager.removeEventListener(listener);
        }
    }

    protected void bindJobManager(JobManager jobManager) {
        this.jobManager = jobManager;
    }

    protected void unbindJobManager(JobManager jobManager) {
        if (this.jobManager == jobManager) {
            this.jobManager = null;
        }
    }

    protected void bindRepository(SlingRepository slingRepository) {
        this.repository = slingRepository;
    }

    protected void unbindRepository(SlingRepository slingRepository) {
        if (this.repository == slingRepository) {
            this.repository = null;
        }
    }

    protected void bindReplicator(Replicator replicator) {
        this.replicator = replicator;
    }

    protected void unbindReplicator(Replicator replicator) {
        if (this.replicator == replicator) {
            this.replicator = null;
        }
    }

    private static class ReplicateOnModifyListener
    implements EventListener {
        private final JobManager jobManager;

        public ReplicateOnModifyListener(JobManager jobManager) {
            this.jobManager = jobManager;
        }

        public void onEvent(EventIterator eventIterator) {
            try {
                ArrayList<ReplicationAction> actions = new ArrayList<ReplicationAction>();
                while (eventIterator.hasNext()) {
                    Event event = eventIterator.nextEvent();
                    String item = event.getPath();
                    LOGGER.trace("Trigger: {} {}", (Object)ReplicateOnModifyListener.getEventTypeStr(event.getType()), (Object)item);
                    if (ReplicateOnModifyListener.skipEvent(event)) {
                        LOGGER.trace("Skipped: {} {}", (Object)ReplicateOnModifyListener.getEventTypeStr(event.getType()), (Object)item);
                        continue;
                    }
                    String path = ReplicateOnModifyListener.getAssetPath(event);
                    boolean isAsset = path.equals(item);
                    ReplicationActionType type = ReplicateOnModifyListener.getReplicationActionType(event, isAsset);
                    ReplicationAction action = new ReplicationAction(type, path, event.getDate(), event.getUserID(), null);
                    int last = actions.size() - 1;
                    if (last >= 0 && ((ReplicationAction)actions.get(last)).getPath().equals(path)) {
                        if (!isAsset) continue;
                        actions.set(last, action);
                        continue;
                    }
                    actions.add(action);
                }
                if (!actions.isEmpty()) {
                    HashMap<String, ArrayList<ReplicationAction>> props = new HashMap<String, ArrayList<ReplicationAction>>();
                    props.put("replication.list", actions);
                    this.jobManager.addJob("com/adobe/cq/dam/dmassetreplicateonmodify", props);
                }
            }
            catch (RepositoryException e) {
                LOGGER.error("Error processing JCR event", (Throwable)e);
            }
        }

        private static boolean skipEvent(Event event) throws RepositoryException {
            JackrabbitEvent je;
            String item = event.getPath();
            if (item == null) {
                return true;
            }
            if (event instanceof JackrabbitEvent && (je = (JackrabbitEvent)event).isExternal()) {
                return true;
            }
            int i = item.lastIndexOf(47);
            if (ReplicateOnModifyListener.isProperty(event.getType()) && i >= 0) {
                String property = item.substring(i + 1);
                return IGNORED_PROPS.contains(property);
            }
            return false;
        }

        private static String getEventTypeStr(int event) {
            switch (event) {
                case 1: {
                    return "NODE_ADDED";
                }
                case 2: {
                    return "NODE_REMOVED";
                }
                case 32: {
                    return "NODE_MOVED";
                }
                case 4: {
                    return "PROPERTY_ADDED";
                }
                case 8: {
                    return "PROPERTY_REMOVED";
                }
                case 16: {
                    return "PROPERTY_CHANGED";
                }
                case 64: {
                    return "PERSIST";
                }
            }
            return "(UNKNOWN)";
        }

        private static ReplicationActionType getReplicationActionType(Event event, boolean isAsset) {
            if (isAsset && event.getType() == 2) {
                return ReplicationActionType.DELETE;
            }
            return ReplicationActionType.ACTIVATE;
        }

        private static String getAssetPath(Event event) throws RepositoryException {
            int i;
            String item = event.getPath();
            boolean isProperty = ReplicateOnModifyListener.isProperty(event.getType());
            if (isProperty && (i = item.lastIndexOf(47)) >= 0) {
                item = item.substring(0, i);
            }
            if (item.endsWith("imageserver/configuration/jcr:content/settings")) {
                return item;
            }
            return JCR_CONTENT.matcher(item).replaceFirst("");
        }

        private static boolean isProperty(int i) {
            return i == 4 || i == 16 || i == 8;
        }
    }

}