/* * #%L * ACS AEM Commons Bundle * %% * Copyright (C) 2016 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.execution.impl.runners; import com.adobe.acs.commons.fam.ActionManager; import com.adobe.acs.commons.fam.ActionManagerFactory; import com.adobe.acs.commons.fam.DeferredActions; import com.adobe.acs.commons.fam.ThrottledTaskRunner; import com.adobe.acs.commons.functions.Consumer; import com.adobe.acs.commons.util.QueryHelper; import com.adobe.acs.commons.workflow.bulk.execution.BulkWorkflowRunner; import com.adobe.acs.commons.workflow.bulk.execution.model.Config; import com.adobe.acs.commons.workflow.bulk.execution.model.Payload; import com.adobe.acs.commons.workflow.bulk.execution.model.SubStatus; import com.adobe.acs.commons.workflow.bulk.execution.model.Workspace; import com.adobe.acs.commons.workflow.synthetic.SyntheticWorkflowModel; import com.adobe.acs.commons.workflow.synthetic.SyntheticWorkflowRunner; import org.apache.commons.lang.StringUtils; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; import org.apache.sling.api.resource.PersistenceException; 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.commons.scheduler.ScheduleOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jcr.RepositoryException; import javax.jcr.Session; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @Component @Service public class FastActionManagerRunnerImpl extends AbstractWorkflowRunner implements BulkWorkflowRunner { private static final Logger log = LoggerFactory.getLogger(FastActionManagerRunnerImpl.class); @Reference private ThrottledTaskRunner throttledTaskRunner; @Reference private ResourceResolverFactory resourceResolverFactoryRef; @Reference private QueryHelper queryHelperRef; @Reference private ActionManagerFactory actionManagerFactoryRef; @Reference private SyntheticWorkflowRunner syntheticWorkflowRunnerRef; @Reference private DeferredActions actionsRef; /** * {@inheritDoc} */ @Override public final Runnable getRunnable(final Config config) { return new FastActionManagerRunnable(config, resourceResolverFactoryRef, queryHelperRef, actionManagerFactoryRef, actionsRef, syntheticWorkflowRunnerRef); } @Override public ScheduleOptions getOptions(Config config) { return null; } @Override public void initialize(QueryHelper queryHelper, Config config) throws PersistenceException, RepositoryException { Workspace workspace = config.getWorkspace(); initialize(workspace, 0); workspace.commit(); } @Override public void start(Workspace workspace) throws PersistenceException { if (!throttledTaskRunner.isRunning()) { throttledTaskRunner.resumeExecution(); } super.start(workspace); } @Override public void stopping(Workspace workspace) throws PersistenceException { stop(workspace); } @Override public void stop(Workspace workspace) throws PersistenceException { throttledTaskRunner.pauseExecution(); super.stop(workspace); } @Override public void stop(Workspace workspace, SubStatus subStatus) throws PersistenceException { throttledTaskRunner.pauseExecution(); super.stop(workspace, subStatus); } @Override public void stopWithError(Workspace workspace) throws PersistenceException { throttledTaskRunner.pauseExecution(); super.stopWithError(workspace); } public void complete(ResourceResolver resourceResolver, String workspacePath, ActionManager manager, int success) throws PersistenceException, RepositoryException { Workspace workspace = resourceResolver.getResource(workspacePath).adaptTo(Workspace.class); workspace.setCompleteCount(success); for (com.adobe.acs.commons.fam.Failure f : manager.getFailureList()) { workspace.addFailure(f.getNodePath(), null, f.getTime()); workspace.incrementFailCount(); } super.complete(workspace); manager.closeAllResolvers(); if (actionManagerFactoryRef != null) { actionManagerFactoryRef.purgeCompletedTasks(); } else { log.warn("Action Manager Factory reference is null. Please purge completed tasks via the JMX console."); } } @Override public void run(Workspace workspace, Payload payload) { if (!throttledTaskRunner.isRunning()) { throttledTaskRunner.resumeExecution(); } } @Override public void complete(Workspace workspace, Payload payload) throws Exception { throw new UnsupportedOperationException("FAM payloads cannot be completed as they are not tracked"); } @Override public void forceTerminate(Workspace workspace, Payload payload) throws Exception { throw new UnsupportedOperationException("FAM jobs cannot be force terminated"); } /** Runner's Runnable **/ private class FastActionManagerRunnable implements Runnable { private final String configPath; private final ResourceResolverFactory resourceResolverFactory; private final QueryHelper queryHelper; private final ActionManagerFactory actionManagerFactory; private final DeferredActions actions; private final SyntheticWorkflowRunner syntheticWorkflowRunner; public FastActionManagerRunnable(Config config, ResourceResolverFactory resourceResolverFactory, QueryHelper queryHelper, ActionManagerFactory actionManagerFactory, DeferredActions actions, SyntheticWorkflowRunner syntheticWorkflowRunner) { this.configPath = config.getPath(); this.resourceResolverFactory = resourceResolverFactory; this.queryHelper = queryHelper; this.actionManagerFactory = actionManagerFactory; this.actions = actions; this.syntheticWorkflowRunner = syntheticWorkflowRunner; } public void run() { ResourceResolver resourceResolver; Resource configResource; try { resourceResolver = resourceResolverFactory.getServiceResourceResolver(AUTH_INFO); configResource = resourceResolver.getResource(configPath); final Config config = configResource.adaptTo(Config.class); final Workspace workspace = config.getWorkspace(); if (StringUtils.isNotBlank(workspace.getActionManagerName()) && actionManagerFactory.hasActionManager(workspace.getActionManagerName())) { log.warn("Action Manager already exists for [ {} ]", workspace.getActionManagerName()); return; } if (config.isUserEventData()) { resourceResolver.adaptTo(Session.class).getWorkspace().getObservationManager().setUserData(config.getUserEventData()); } /** Collect and initialize the workspace **/ final List<Resource> resources; resources = queryHelper.findResources(resourceResolver, config.getQueryType(), config.getQueryStatement(), config.getRelativePath()); // Reset FAM tracking actionManagerFactory.purgeCompletedTasks(); final ActionManager manager = actionManagerFactory.createTaskManager( "Bulk Workflow Manager @ " + config.getPath(), resourceResolver, config.getBatchSize()); final SyntheticWorkflowModel model = syntheticWorkflowRunner.getSyntheticWorkflowModel( resourceResolver, config.getWorkflowModelId(), true); workspace.setTotalCount(resources.size()); workspace.setActionManagerName(manager.getName()); workspace.commit(); /** Begin the work of processing the results **/ final String workspacePath = workspace.getPath(); final int retryCount = config.getRetryCount(); final int retryPause = config.getInterval(); final AtomicInteger processed = new AtomicInteger(0); final int total = resources.size(); final AtomicInteger success = new AtomicInteger(0); for (final Resource resource : resources) { final String path = resource.getPath(); manager.deferredWithResolver(new Consumer<ResourceResolver>() { @Override public void accept(ResourceResolver r) throws Exception { try { manager.setCurrentItem(path); if (retryCount > 0) { try { actions.retryAll(retryCount, retryPause, actions.startSyntheticWorkflows(model)).accept(r, path); success.incrementAndGet(); } catch (Exception e) { log.warn("Could not process [ {} ] with [ " + retryCount + " ] retries", path, e); // Must throw the exception so defferedWithResolver and pick up the failure throw e; } } else { try { actions.startSyntheticWorkflows(model).accept(r, path); success.incrementAndGet(); } catch (Exception e) { log.warn("Could not process [ {} ]", path, e); // Must throw the exception so defferedWithResolver and pick up the failure throw e; } } } finally { if (processed.incrementAndGet() == total) { complete(r, workspacePath, manager, success.get()); } } } }); } } catch (Exception e) { log.error("Error occurred while processing Fast Action Manager Synthetic Workflow via Bulk Workflow Manager", e); } } } }