/*
* #%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.util.QueryHelper;
import com.adobe.acs.commons.workflow.bulk.execution.BulkWorkflowRunner;
import com.adobe.acs.commons.workflow.bulk.execution.model.Status;
import com.adobe.acs.commons.workflow.bulk.execution.model.SubStatus;
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.PayloadGroup;
import com.adobe.acs.commons.workflow.bulk.execution.model.Workspace;
import com.day.cq.commons.jcr.JcrUtil;
import org.apache.jackrabbit.commons.JcrUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
public abstract class AbstractWorkflowRunner implements BulkWorkflowRunner {
private static final Logger log = LoggerFactory.getLogger(AbstractWorkflowRunner.class);
private static final int SAVE_THRESHOLD = 1000;
protected static final String SERVICE_NAME = "bulk-workflow-runner";
protected static final Map<String, Object> AUTH_INFO;
static {
AUTH_INFO = Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, (Object) SERVICE_NAME);
}
/**
* {@inheritDoc}
*/
@Override
public void initialize(QueryHelper queryHelper, Config config) throws
PersistenceException, RepositoryException {
// Query for all candidate resources
final ResourceResolver resourceResolver = config.getResourceResolver();
final List<Resource> resources = queryHelper.findResources(resourceResolver,
config.getQueryType(),
config.getQueryStatement(),
config.getRelativePath());
int total = 0;
// Create Workspace Node
Node workspace = JcrUtils.getOrAddNode(config.getResource().adaptTo(Node.class), Workspace.NN_WORKSPACE, Workspace.NT_UNORDERED);
// Create the PayloadGroups Launchpad node; this simply points to the first to process
Node currentPayloadGroup = JcrUtils.getOrCreateByPath(workspace, Workspace.NN_PAYLOADS_LAUNCHPAD, true, Workspace.NT_UNORDERED, Workspace.NT_UNORDERED, false);
// Set the first Payload Group to be the launchpad node
JcrUtil.setProperty(workspace, Workspace.PN_ACTIVE_PAYLOAD_GROUPS, new String[]{PayloadGroup.dereference(currentPayloadGroup.getPath())});
// No begin populating the actual PayloadGroup nodes
ListIterator<Resource> itr = resources.listIterator();
while (itr.hasNext()) {
// Increment to a new PayloadGroup as needed
if (total % config.getBatchSize() == 0 && itr.hasNext()) {
// payload group is complete; save...
Node nextPayloadGroup = JcrUtils.getOrCreateByPath(workspace, Workspace.NN_PAYLOADS, true, Workspace.NT_UNORDERED, Workspace.NT_UNORDERED, false);
JcrUtil.setProperty(currentPayloadGroup, PayloadGroup.PN_NEXT, PayloadGroup.dereference(nextPayloadGroup.getPath()));
currentPayloadGroup = nextPayloadGroup;
}
// Process the payload
Resource payload = itr.next();
Node payloadNode = JcrUtils.getOrCreateByPath(currentPayloadGroup, Payload.NN_PAYLOAD, true, Workspace.NT_UNORDERED, Workspace.NT_UNORDERED, false);
JcrUtil.setProperty(payloadNode, "path", Payload.dereference(payload.getPath()));
log.debug("Created payload with search result [ {} ]", payload.getPath());
if (++total % SAVE_THRESHOLD == 0 || !itr.hasNext()) {
resourceResolver.commit();
}
} // while
if (total > 0) {
config.getWorkspace().getRunner().initialize(config.getWorkspace(), total);
config.commit();
log.info("Completed initialization of Bulk Workflow Manager");
} else {
throw new IllegalArgumentException("Query returned zero results.");
}
}
public void initialize(Workspace workspace, int totalCount) throws PersistenceException {
workspace.setInitialized(true);
workspace.setTotalCount(totalCount);
workspace.commit();
}
public void start(Workspace workspace) throws PersistenceException {
workspace.setStatus(Status.RUNNING);
if (workspace.getStartedAt() == null) {
workspace.setStartedAt(Calendar.getInstance());
}
workspace.commit();
}
public void stopping(Workspace workspace) throws PersistenceException {
workspace.setStatus(Status.RUNNING, SubStatus.STOPPING);
workspace.commit();
}
public void stop(Workspace workspace) throws PersistenceException {
workspace.setStatus(Status.STOPPED);
workspace.setStoppedAt(Calendar.getInstance());
workspace.commit();
}
public void stop(Workspace workspace, SubStatus subStatus) throws PersistenceException {
if (subStatus != null) {
workspace.setStatus(Status.STOPPED, subStatus);
} else {
workspace.setStatus(Status.STOPPED);
}
workspace.setStoppedAt(Calendar.getInstance());
workspace.commit();
}
public void stopWithError(Workspace workspace) throws PersistenceException {
workspace.setStatus(Status.STOPPED, SubStatus.ERROR);
workspace.setStoppedAt(Calendar.getInstance());
workspace.commit();
}
public void complete(Workspace workspace) throws PersistenceException {
workspace.setStatus(Status.COMPLETED);
workspace.setCompletedAt(Calendar.getInstance());
workspace.commit();
}
public void run(Workspace workspace, Payload payload) {
payload.setStatus(Status.RUNNING);
}
public void complete(Workspace workspace, Payload payload) throws Exception {
// Remove active payload
if (workspace != null) {
workspace.removeActivePayload(payload);
// Increment the complete count
workspace.incrementCompleteCount();
} else {
log.warn("Unable to processing complete for payload [ {} ~> {} ]", payload.getPath(), payload.getPayloadPath());
}
}
public void fail(Workspace workspace, Payload payload) throws Exception {
// Remove active payload
workspace.removeActivePayload(payload);
// Increment the complete count
workspace.incrementFailCount();
// Track the failure details
workspace.addFailure(payload);
}
public abstract void forceTerminate(Workspace workspace, Payload payload) throws Exception;
}