UpgradeContentCleanup.java 9.48 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  javax.jcr.Node
 *  javax.jcr.NodeIterator
 *  javax.jcr.Session
 *  javax.jcr.Workspace
 *  javax.jcr.query.Query
 *  javax.jcr.query.QueryManager
 *  javax.jcr.query.QueryResult
 *  org.apache.felix.scr.annotations.Activate
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.ConfigurationPolicy
 *  org.apache.felix.scr.annotations.Deactivate
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.PropertyUnbounded
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.sling.commons.osgi.PropertiesUtil
 *  org.apache.sling.jcr.api.SlingRepository
 *  org.apache.sling.launchpad.api.StartupHandler
 *  org.apache.sling.launchpad.api.StartupMode
 *  org.osgi.framework.BundleContext
 *  org.osgi.framework.InvalidSyntaxException
 *  org.osgi.framework.ServiceEvent
 *  org.osgi.framework.ServiceListener
 *  org.osgi.service.component.ComponentContext
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.adobe.cq.upgrades.cleanup.impl;

import java.util.Arrays;
import java.util.Dictionary;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Session;
import javax.jcr.Workspace;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
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.Property;
import org.apache.felix.scr.annotations.PropertyUnbounded;
import org.apache.felix.scr.annotations.Reference;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.jcr.api.SlingRepository;
import org.apache.sling.launchpad.api.StartupHandler;
import org.apache.sling.launchpad.api.StartupMode;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(configurationFactory=1, policy=ConfigurationPolicy.REQUIRE, metatype=1, label="Content Cleanup Component", description="Deletes content while upgrading, before the package manager starts")
public class UpgradeContentCleanup
implements ServiceListener {
    private final Logger log;
    @Reference
    protected SlingRepository repository;
    @Reference
    private StartupHandler startupHandler;
    private boolean isListening;
    private final AtomicInteger execCounter;
    public static final String JOB_TOPIC = "com/adobe/cq/upgrade/cleanup/delete";
    public static final String JOB_PATH = "path";
    @Property(unbounded=PropertyUnbounded.ARRAY, label="Snapshot path regular expressions", description="Regular expressions that define which paths are allowed to be deleted")
    public static final String PROP_DELETE_PATH_REGEXPS = "delete.path.regexps";
    private Pattern[] pathPatterns;
    @Property(label="Delete selection query", description="SQL2 query that selects candidates for deletion")
    public static final String PROP_DELETE_QUERY = "delete.sql2.query";
    private String deleteQuery;

    public UpgradeContentCleanup() {
        this.log = LoggerFactory.getLogger(this.getClass());
        this.execCounter = new AtomicInteger();
    }

    @Activate
    protected void activate(ComponentContext ctx) throws InvalidSyntaxException {
        this.isListening = false;
        StartupMode sm = this.startupHandler.getMode();
        if (sm != StartupMode.UPDATE) {
            this.log.info("Startup mode is {}, nothing to do", (Object)sm);
            return;
        }
        this.log.info("Startup mode is {}, cleanup will be done", (Object)sm);
        String vaultServiceClass = "org.apache.jackrabbit.vault.packaging.Packaging";
        String filter = "(objectClass=org.apache.jackrabbit.vault.packaging.Packaging)";
        ctx.getBundleContext().addServiceListener((ServiceListener)this, "(objectClass=org.apache.jackrabbit.vault.packaging.Packaging)");
        this.isListening = true;
        this.deleteQuery = PropertiesUtil.toString(ctx.getProperties().get("delete.sql2.query"), (String)null);
        String[] expr = PropertiesUtil.toStringArray(ctx.getProperties().get("delete.path.regexps"), (String[])null);
        this.pathPatterns = new Pattern[expr.length];
        for (int i = 0; i < expr.length; ++i) {
            this.pathPatterns[i] = Pattern.compile(expr[i]);
        }
        this.log.info("deleteQuery={}", (Object)this.deleteQuery);
        this.log.info("deletable paths regexp={}", Arrays.asList(this.pathPatterns));
    }

    @Deactivate
    protected void deactivate(ComponentContext ctx) {
        if (this.isListening) {
            ctx.getBundleContext().removeServiceListener((ServiceListener)this);
        }
    }

    public void serviceChanged(ServiceEvent event) {
        if (event.getType() == 1) {
            this.doCleanup();
        }
    }

    private void deleteAsynchronously(final String path) {
        this.log.info("Starting background job to delete {}", (Object)path);
        Thread t = new Thread("Delete " + path){

            /*
             * WARNING - Removed try catching itself - possible behaviour change.
             */
            public void run() {
                block7 : {
                    Session s = null;
                    try {
                        s = UpgradeContentCleanup.this.repository.loginAdministrative(null);
                        if (s.nodeExists(path)) {
                            s.getNode(path).remove();
                            s.save();
                            UpgradeContentCleanup.this.log.info("Deleted {}", (Object)path);
                            break block7;
                        }
                        UpgradeContentCleanup.this.log.warn("Path to delete not found: {}", (Object)path);
                    }
                    catch (Exception e) {
                        UpgradeContentCleanup.this.log.warn("Exception while deleting " + path, (Throwable)e);
                    }
                    finally {
                        if (s != null) {
                            s.logout();
                        }
                    }
                }
            }
        };
        t.setDaemon(true);
        t.start();
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private void doCleanup() {
        block12 : {
            try {
                if (this.execCounter.incrementAndGet() != 1) {
                    this.log.info("Concurrent execution detected, won't do anything");
                    break block12;
                }
                Session s = this.repository.loginAdministrative(null);
                try {
                    Query q = s.getWorkspace().getQueryManager().createQuery(this.deleteQuery, "JCR-SQL2");
                    NodeIterator it = q.execute().getNodes();
                    int deleted = 0;
                    int ignored = 0;
                    String targetFolder = "pre-upgrade-cleanup-" + System.currentTimeMillis();
                    String targetPath = s.getNode("/tmp").addNode(targetFolder).getPath();
                    s.save();
                    while (it.hasNext()) {
                        Node n = it.nextNode();
                        if (this.okToRemove(n.getPath())) {
                            String moveTo = targetPath + "/" + deleted;
                            this.log.info("Moving {} to {}", (Object)n.getPath(), (Object)moveTo);
                            s.getWorkspace().move(n.getPath(), moveTo);
                            ++deleted;
                            continue;
                        }
                        ++ignored;
                    }
                    this.log.info("{} nodes deleted, {} ignored", (Object)deleted, (Object)ignored);
                    if (deleted > 0) {
                        this.deleteAsynchronously(targetPath);
                    }
                }
                finally {
                    s.save();
                    s.logout();
                }
            }
            catch (Exception e) {
                this.log.warn("Exception in doCleanup()", (Throwable)e);
            }
            finally {
                this.execCounter.decrementAndGet();
            }
        }
    }

    private boolean okToRemove(String path) {
        for (Pattern p : this.pathPatterns) {
            if (!p.matcher(path).matches()) continue;
            this.log.debug("Path {} matches {}, ok to remove", (Object)path, (Object)p);
            return true;
        }
        this.log.debug("Path {} does not match any pattern, won't remove", (Object)path);
        return false;
    }

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

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

    protected void bindStartupHandler(StartupHandler startupHandler) {
        this.startupHandler = startupHandler;
    }

    protected void unbindStartupHandler(StartupHandler startupHandler) {
        if (this.startupHandler == startupHandler) {
            this.startupHandler = null;
        }
    }

}