/*
* #%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.ThrottledTaskRunner;
import com.adobe.acs.commons.workflow.bulk.execution.BulkWorkflowRunner;
import com.adobe.acs.commons.workflow.bulk.execution.model.*;
import com.adobe.acs.commons.workflow.synthetic.SyntheticWorkflowModel;
import com.adobe.acs.commons.workflow.synthetic.SyntheticWorkflowRunner;
import com.day.cq.workflow.WorkflowException;
import org.apache.commons.collections.ListUtils;
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.apache.sling.commons.scheduler.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Session;
import java.util.List;
@Component
@Service
public class SyntheticWorkflowRunnerImpl extends AbstractWorkflowRunner implements BulkWorkflowRunner {
private static final Logger log = LoggerFactory.getLogger(SyntheticWorkflowRunnerImpl.class);
@Reference
private ResourceResolverFactory resourceResolverFactory;
@Reference
private Scheduler scheduler;
@Reference
private SyntheticWorkflowRunner swr;
@Reference
private ThrottledTaskRunner throttledTaskRunner;
@Override
public final Runnable getRunnable(final Config config) {
return new SyntheticWorkflowRunnable(config, scheduler, resourceResolverFactory, swr, throttledTaskRunner);
}
@Override
public ScheduleOptions getOptions(Config config) {
ScheduleOptions options = scheduler.NOW();
options.canRunConcurrently(false);
options.onLeaderOnly(true);
options.name(config.getWorkspace().getJobName());
return options;
}
@Override
public void forceTerminate(Workspace workspace, Payload payload) throws PersistenceException {
workspace.setStatus(Status.FORCE_TERMINATED);
}
@Override
public void complete(Workspace workspace, Payload payload) throws Exception {
// Remove active payload
super.complete(workspace, payload);
payload.setStatus(Status.COMPLETED);
}
@Override
public void run(Workspace workspace, Payload payload) {
super.run(workspace, payload);
}
/** Runner's Runnable **/
private class SyntheticWorkflowRunnable implements Runnable {
private final ResourceResolverFactory resourceResolverFactory;
private final SyntheticWorkflowRunner syntheticWorkflowRunner;
private final ThrottledTaskRunner throttledTaskRunner;
private final Scheduler scheduler;
private String configPath;
public SyntheticWorkflowRunnable(Config config,
Scheduler scheduler,
ResourceResolverFactory resourceResolverFactory,
SyntheticWorkflowRunner syntheticWorkflowRunner,
ThrottledTaskRunner throttledTaskRunner) {
this.configPath = config.getPath();
this.resourceResolverFactory = resourceResolverFactory;
this.syntheticWorkflowRunner = syntheticWorkflowRunner;
this.throttledTaskRunner = throttledTaskRunner;
this.scheduler = scheduler;
}
public void run() {
ResourceResolver serviceResourceResolver = null;
Resource configResource;
long start = System.currentTimeMillis();
int total = 0;
boolean stopped = false;
try {
serviceResourceResolver = resourceResolverFactory.getServiceResourceResolver(AUTH_INFO);
configResource = serviceResourceResolver.getResource(configPath);
final Config config = configResource.adaptTo(Config.class);
final Workspace workspace = config.getWorkspace();
if (workspace.isStopped()) {
return;
}
try {
SyntheticWorkflowModel model = syntheticWorkflowRunner.getSyntheticWorkflowModel(serviceResourceResolver, config.getWorkflowModelId(), true);
if (config.isUserEventData()) {
serviceResourceResolver.adaptTo(Session.class).getWorkspace().getObservationManager().setUserData(config.getUserEventData());
log.debug("Set JCR Sessions user-event-data to [ {} ]", config.getUserEventData());
}
PayloadGroup payloadGroup = null;
if (workspace.getActivePayloadGroups().size() > 0) {
payloadGroup = workspace.getActivePayloadGroups().get(0);
}
while (payloadGroup != null) {
List<Payload> payloads = workspace.getActivePayloads();
if (payloads.size() == 0) {
// payloads size is 0, so onboard next payload group
payloadGroup = onboardNextPayloadGroup(workspace, payloadGroup);
if (payloadGroup != null) {
payloads = onboardNextPayloads(workspace, payloadGroup);
}
}
// Safety check; if payloads comes in null then immediately break from loop as there is no work to do
if (payloads == null || payloads.size() == 0) {
break;
}
for (Payload payload : payloads) {
if (workspace.isStopping() || workspace.isStopped()) {
stop(workspace);
stopped = true;
break;
}
try {
if (config.isAutoThrottle()) {
// Wait before starting more work
throttledTaskRunner.waitForLowCpuAndLowMemory();
}
long processStart = System.currentTimeMillis();
swr.execute(serviceResourceResolver, payload.getPayloadPath(), model, false, false);
complete(workspace, payload);
log.info("Processed [ {} ] in {} ms", payload.getPayloadPath(), System.currentTimeMillis() - processStart);
} catch (WorkflowException e) {
fail(workspace, payload);
log.warn("Synthetic Workflow could not process [ {} ]", payload.getPath(), e);
} catch (Exception e) {
// Complete call failed; consider it failed
log.warn("Complete call on [ {} ] failed", payload.getPath(), e);
fail(workspace, payload);
}
total++;
} // end for
workspace.commit();
if (stopped) {
log.info("Bulk Synthetic Workflow run has been stopped.");
break;
}
} // end while
// Stop check in case a STOP request is made that breaks the while loop
if (!stopped) {
complete(workspace);
}
log.info("Grand total of [ {} ] payloads saved in {} ms", total, System.currentTimeMillis() - start);
} catch (Exception e) {
log.error("Error processing Bulk Synthetic Workflow execution.", e);
}
} catch (Exception e) {
log.error("Error processing Bulk Synthetic Workflow execution.", e);
} finally {
if (serviceResourceResolver != null) {
serviceResourceResolver.close();
}
}
}
private PayloadGroup onboardNextPayloadGroup(Workspace workspace, PayloadGroup currentPayloadGroup) throws PersistenceException {
PayloadGroup nextPayloadGroup = currentPayloadGroup.getNextPayloadGroup();
workspace.removeActivePayloadGroup(currentPayloadGroup);
if (nextPayloadGroup != null) {
workspace.addActivePayloadGroup(nextPayloadGroup);
}
return nextPayloadGroup;
}
private List<Payload> onboardNextPayloads(Workspace workspace, PayloadGroup payloadGroup) throws PersistenceException {
if (payloadGroup == null) {
return ListUtils.EMPTY_LIST;
}
List<Payload> payloads = payloadGroup.getPayloads();
if (payloads.size() > 0) {
workspace.addActivePayloads(payloads);
}
return payloads;
}
}
}