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

com.adobe.acs.commons.workflow.bulk.removal.impl.WorkflowInstanceRemoverScheduler Maven / Gradle / Ivy

There is a newer version: 6.6.0
Show newest version
/*
 * #%L
 * ACS AEM Commons Bundle
 * %%
 * Copyright (C) 2015 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.workflow.bulk.removal.impl;

import com.adobe.acs.commons.util.InfoWriter;
import com.adobe.acs.commons.workflow.bulk.removal.WorkflowInstanceRemover;
import com.adobe.acs.commons.workflow.bulk.removal.WorkflowRemovalException;
import com.adobe.acs.commons.workflow.bulk.removal.WorkflowRemovalForceQuitException;

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.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

@Component(
        label = "ACS AEM Commons - Workflow Instance Remover - Scheduled Service",
        metatype = true,
        configurationFactory = true,
        policy = ConfigurationPolicy.REQUIRE
)
@Properties({
        @Property(
                label = "Cron expression defining when this Scheduled Service will run",
                description = "[12:01am daily = 0 1 0 ? * *]; see www.cronmaker.com",
                name = "scheduler.expression",
                value = "0 1 0 ? * *"
        ),
        @Property(
                label = "Allow concurrent executions",
                description = "Allow concurrent executions of this Scheduled Service",
                name = "scheduler.concurrent",
                boolValue = false,
                propertyPrivate = true
        ),
        @Property(
                name = "webconsole.configurationFactory.nameHint",
                propertyPrivate = true,
                value = "Runs at '{scheduler.expression}' on models [{workflow.models}] with status [{workflow.statuses}]"
        )
})
@Service
public class WorkflowInstanceRemoverScheduler implements Runnable {
    private static final Logger log = LoggerFactory.getLogger(WorkflowInstanceRemoverScheduler.class);

    @Reference
    private ResourceResolverFactory resourceResolverFactory;

    @Reference
    private WorkflowInstanceRemover workflowInstanceRemover;

    private static final String SERVICE_NAME = "workflow-remover";
    private static final Map AUTH_INFO;

    static {
        AUTH_INFO = Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, (Object) SERVICE_NAME);
    }

    private static final String[] DEFAULT_WORKFLOW_STATUSES = {"COMPLETED", "ABORTED"};

    private List statuses = new ArrayList();

    @Property(label = "Workflow Status",
            description = "Only remove Workflow Instances that have one of these statuses.",
            value = { "COMPLETED", "ABORTED" })
    public static final String PROP_WORKFLOW_STATUSES = "workflow.statuses";


    private static final String[] DEFAULT_WORKFLOW_MODELS = {};

    private List models = new ArrayList();

    @Property(label = "Workflow Models",
            description = "Only remove Workflow Instances that belong to one of these WF Models.",
            cardinality = Integer.MAX_VALUE,
            value = { })
    public static final String PROP_WORKFLOW_MODELS = "workflow.models";


    private static final String[] DEFAULT_WORKFLOW_PAYLOADS = {};

    private List payloads = new ArrayList();

    @Property(label = "Payload Patterns",
            description = "Only remove Workflow Instances whose payloads match one of these regex patterns",
            cardinality = Integer.MAX_VALUE,
            value = { })
    public static final String PROP_WORKFLOW_PAYLOADS = "workflow.payloads";


    private Calendar olderThan = null;

    @Property(label = "Older Than UTC TS",
            description = "Only remove Workflow Instances whose payloads are older than this UTC Time in Millis",
            longValue = 0)
    public static final String PROP_WORKFLOWS_OLDER_THAN = "workflow.older-than";


    private static final int DEFAULT_BATCH_SIZE = 1000;
    private int batchSize = DEFAULT_BATCH_SIZE;
    @Property(label = "Batch Size",
            description = "Save removals to JCR in batches of this defined size.",
            intValue = DEFAULT_BATCH_SIZE)
    public static final String PROP_BATCH_SIZE = "batch-size";


    private static final int DEFAULT_MAX_DURATION = 0;
    private int maxDuration = DEFAULT_MAX_DURATION;
    @Property(label = "Max duration (in minutes)",
            description = "Max number of minutes this workflow removal process can execute. 0 for no limit. "
                    + "[ Default: 0 ]",
            intValue = DEFAULT_MAX_DURATION)
    public static final String PROP_MAX_DURATION = "max-duration";

    @Override
    @SuppressWarnings("squid:S2142")
    public final void run() {

        try (ResourceResolver adminResourceResolver = resourceResolverFactory.getServiceResourceResolver(AUTH_INFO)){

            final long start = System.currentTimeMillis();

            int count = workflowInstanceRemover.removeWorkflowInstances(
                    adminResourceResolver,
                    models,
                    statuses,
                    payloads,
                    olderThan, 
                    batchSize,
                    maxDuration);

            if (log.isInfoEnabled()) {
                log.info("Removed [ {} ] Workflow instances in {} ms", count, System.currentTimeMillis() - start);
            }

        } catch (LoginException e) {
            log.error("Login Exception when getting admin resource resolver", e);
        } catch (PersistenceException e) {
            log.error("Persistence Exception when saving Workflow Instances removal", e);
        } catch (WorkflowRemovalException e) {
            log.error("Workflow Removal exception during Workflow Removal", e);
        } catch (InterruptedException e) {
            log.error("Interrupted Exception during Workflow Removal", e);
        } catch (WorkflowRemovalForceQuitException e) {
            log.info("Workflow Removal force quit", e);
        }
    }

    private List arrayToList(String[] array) {
        List list = new ArrayList();

        for (String element : array) {
            if (StringUtils.isNotBlank(element)) {
                list.add(element);
            }
        }

        return list;
    }

    @Activate
    protected final void activate(final Map config) {

        statuses = arrayToList(PropertiesUtil.toStringArray(config.get(PROP_WORKFLOW_STATUSES), DEFAULT_WORKFLOW_STATUSES));

        models = arrayToList(PropertiesUtil.toStringArray(config.get(PROP_WORKFLOW_MODELS), DEFAULT_WORKFLOW_MODELS));

        final String[] payloadsArray =
                PropertiesUtil.toStringArray(config.get(PROP_WORKFLOW_PAYLOADS), DEFAULT_WORKFLOW_PAYLOADS);

        for (final String payload : payloadsArray) {
            if (StringUtils.isNotBlank(payload)) {
                final Pattern p = Pattern.compile(payload);
                if (p != null) {
                    payloads.add(p);
                }
            }
        }

        final Long olderThanTs = PropertiesUtil.toLong(config.get(PROP_WORKFLOWS_OLDER_THAN), 0);

        if (olderThanTs > 0) {
            olderThan = Calendar.getInstance();
            olderThan.setTimeInMillis(olderThanTs);
        }
        
        batchSize = PropertiesUtil.toInteger(config.get(PROP_BATCH_SIZE), DEFAULT_BATCH_SIZE);
        if (batchSize < 1) {
            batchSize = DEFAULT_BATCH_SIZE;
        }

        maxDuration = PropertiesUtil.toInteger(config.get(PROP_MAX_DURATION), DEFAULT_MAX_DURATION);

        final InfoWriter iw = new InfoWriter();
        iw.title("Workflow Instance Removal Configuration");
        iw.message("Workflow status: {}", statuses);
        iw.message("Workflow models: {}", models);
        iw.message("Payloads: {}", Arrays.asList(payloadsArray));
        iw.message("Older than: {}", olderThan);
        iw.message("Batch size: {}", batchSize);
        iw.message("Max Duration (minutes): {}", maxDuration);
        iw.end();

        log.info(iw.toString());
    }

    @Deactivate
    protected final void deactivate(final Map config) {
        olderThan = null;
        statuses = new ArrayList();
        models = new ArrayList();
        payloads = new ArrayList();
        batchSize = DEFAULT_BATCH_SIZE;
        maxDuration = DEFAULT_MAX_DURATION;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy