Workflow54ModelUpgrade.java 18.1 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  com.day.cq.commons.jcr.JcrUtil
 *  com.day.cq.security.Authorizable
 *  com.day.cq.security.UserManager
 *  com.day.cq.workflow.WorkflowException
 *  com.day.cq.workflow.WorkflowService
 *  com.day.cq.workflow.WorkflowSession
 *  com.day.cq.workflow.metadata.MetaDataMap
 *  com.day.cq.workflow.model.WorkflowModel
 *  com.day.cq.workflow.model.WorkflowNode
 *  com.day.cq.workflow.model.WorkflowTransition
 *  com.day.text.Text
 *  javax.jcr.ItemVisitor
 *  javax.jcr.Node
 *  javax.jcr.NodeIterator
 *  javax.jcr.Property
 *  javax.jcr.RepositoryException
 *  javax.jcr.Session
 *  javax.jcr.Workspace
 *  javax.jcr.query.Query
 *  javax.jcr.query.QueryManager
 *  javax.jcr.query.QueryResult
 *  javax.jcr.util.TraversingItemVisitor
 *  javax.jcr.util.TraversingItemVisitor$Default
 *  javax.jcr.version.Version
 *  javax.jcr.version.VersionManager
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.day.cq.compat.codeupgrade.impl.cq56;

import com.day.cq.commons.jcr.JcrUtil;
import com.day.cq.compat.codeupgrade.internal.api.ProgressInfoProvider;
import com.day.cq.security.Authorizable;
import com.day.cq.security.UserManager;
import com.day.cq.workflow.WorkflowException;
import com.day.cq.workflow.WorkflowService;
import com.day.cq.workflow.WorkflowSession;
import com.day.cq.workflow.metadata.MetaDataMap;
import com.day.cq.workflow.model.WorkflowModel;
import com.day.cq.workflow.model.WorkflowNode;
import com.day.cq.workflow.model.WorkflowTransition;
import com.day.text.Text;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.jcr.ItemVisitor;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Workspace;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
import javax.jcr.util.TraversingItemVisitor;
import javax.jcr.version.Version;
import javax.jcr.version.VersionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class Workflow54ModelUpgrade
implements ProgressInfoProvider {
    private final Logger log;
    private String progressInfo;
    private static final String MODELS_ROOT_PATH = "/etc/workflow/models";
    private static final String MODELS_QUERY = "select * from [cq:WorkflowModel]";
    private static final String MODEL_TEMPLATE = "/libs/cq/workflow/templates/model";
    private static final String MODEL_RESOURCE_TYPE = "cq/workflow/components/pages/model";
    private static final String MODEL_PATH_SUFFIX = "/jcr:content/model";
    private static final String WORKFLOW_TAG_NAMESPACE = "workflow:";
    private static final String NT_PAGE = "cq:Page";
    private static final String NT_PAGE_CONTENT = "cq:PageContent";
    private static final String RT_PARTICIPANT_STEP = "cq/workflow/components/model/participant";
    private static final String RT_PROCESS_STEP = "cq/workflow/components/model/process";
    private static final String RT_DYNAMIC_PARTICIPANT_STEP = "cq/workflow/components/model/dynamic_participant";
    private static final String RT_AND_SPLIT = "cq/workflow/components/model/and";
    private static final String RT_OR_SPLIT = "cq/workflow/components/model/or";
    private static final String RT_CONTAINER_STEP = "cq/workflow/components/model/container";
    private static final String RT_PARSYS = "cq/flow/components/parsys";
    private Pattern plainScriptRegex;
    private UserManager userManager;
    private Map<String, String> upgradedModelIdMapping;
    private boolean doUpgrade;

    public Workflow54ModelUpgrade() {
        this.log = LoggerFactory.getLogger(this.getClass());
        this.progressInfo = "No info yet";
        this.plainScriptRegex = Pattern.compile(".*function\\s*check\\(\\).*", 32);
        this.doUpgrade = false;
        this.doUpgrade = true;
    }

    public Workflow54ModelUpgrade(boolean doUpgrade) {
        this.log = LoggerFactory.getLogger(this.getClass());
        this.progressInfo = "No info yet";
        this.plainScriptRegex = Pattern.compile(".*function\\s*check\\(\\).*", 32);
        this.doUpgrade = false;
        this.doUpgrade = doUpgrade;
    }

    @Override
    public String getProgressInfo() {
        return this.progressInfo;
    }

    void setProgressInfo(String info) {
        this.progressInfo = info;
        this.log.info(this.progressInfo);
    }

    void doUpgrade(Session s, WorkflowService workflowService, UserManager um) throws RepositoryException, WorkflowException {
        if (!this.doUpgrade) {
            this.setProgressInfo("Skipping Workflow54ModelUpgrade");
            return;
        }
        this.setProgressInfo("Upgrading workflow models...");
        this.upgradedModelIdMapping = new HashMap<String, String>();
        WorkflowSession workflowSession = workflowService.getWorkflowSession(s);
        NodeIterator models = s.getWorkspace().getQueryManager().createQuery("select * from [cq:WorkflowModel]", "JCR-SQL2").execute().getNodes();
        this.userManager = um;
        int counter = 0;
        int upgraded = 0;
        while (models.hasNext()) {
            this.setProgressInfo("" + counter + " workflow models checked, " + upgraded + " upgraded");
            ++counter;
            Node modelNode = models.nextNode();
            if (s.getNode(Text.getRelativeParent((String)modelNode.getPath(), (int)2)).isNodeType("cq:Page")) {
                this.log.info("Workflow model already in page-based format, upgrade not needed: {}", (Object)modelNode.getPath());
                if (!modelNode.getPath().endsWith("/jcr:content/model")) continue;
                int pos = modelNode.getPath().lastIndexOf("/jcr:content/model");
                this.adjustInstances(modelNode.getPath().substring(0, pos), modelNode.getPath(), s);
                continue;
            }
            WorkflowModel model = workflowSession.getModel(modelNode.getPath());
            if (model == null) continue;
            this.upgradeModel(s, model, workflowSession);
            ++upgraded;
        }
        this.setProgressInfo("Adjusting references");
        s.getNode("/etc/workflow/models").accept((ItemVisitor)new WorkflowReferenceSearch());
        if (upgraded > 0) {
            this.setProgressInfo("Finished upgrading " + upgraded + " workflow models (out of " + counter + ")");
        } else {
            this.setProgressInfo("No workflow models found to upgrade");
        }
    }

    private void upgradeModel(Session session, WorkflowModel model, WorkflowSession workflowSession) {
        this.log.info("Starting upgrade of workflow model: {}", (Object)model.getId());
        try {
            Node modelPage = JcrUtil.createUniquePath((String)model.getId(), (String)"cq:Page", (Session)session);
            Node modelContent = modelPage.addNode("jcr:content", "cq:PageContent");
            modelContent.setProperty("jcr:title", model.getTitle());
            modelContent.setProperty("jcr:description", model.getDescription());
            modelContent.setProperty("sling:resourceType", "cq/workflow/components/pages/model");
            modelContent.setProperty("cq:template", "/libs/cq/workflow/templates/model");
            this.log.info("Workflow model page created: ", (Object)modelPage.getPath());
            String tags = (String)model.getMetaDataMap().get("tags", String.class);
            if (tags != null && tags.length() > 0) {
                Object[] tagValues = tags.split(",");
                for (int i = 0; i < tagValues.length; ++i) {
                    tagValues[i] = "workflow:" + tagValues[i].replace('.', '/');
                }
                modelContent.setProperty("cq:tags", (String[])tagValues);
                this.log.info("Workflow model tags upgraded: {}", tagValues);
            }
            Node flow = modelContent.addNode("flow");
            flow.setProperty("sling:resourceType", "foundation/components/parsys");
            Stack<Node> flowStack = new Stack<Node>();
            flowStack.push(flow);
            WorkflowNode start = model.getRootNode();
            this.traverseRoute((WorkflowTransition)start.getTransitions().get(0), flowStack, session);
            session.save();
            this.log.info("Creating version for upgraded model: ", (Object)(modelContent.getPath() + "/model"));
            String targetModelPath = modelContent.getPath() + "/model";
            session.getWorkspace().copy(model.getId(), targetModelPath);
            if (!session.getWorkspace().getVersionManager().isCheckedOut(targetModelPath)) {
                session.getWorkspace().getVersionManager().checkout(targetModelPath);
            }
            session.getWorkspace().getVersionManager().checkin(targetModelPath);
            session.save();
            this.log.info("Version for upgraded model created: {}", (Object)(modelContent.getPath() + "/model"));
            this.upgradedModelIdMapping.put(model.getId(), targetModelPath);
            this.log.info("Setting original workflow model to deleted: {}", (Object)model.getId());
            model.getMetaDataMap().put((Object)"deleted", (Object)"true");
            workflowSession.deployModel(model);
            this.adjustLauncherConfig(model.getId(), targetModelPath, session);
        }
        catch (Exception e) {
            this.log.error("Upgrade of workflow model {} failed :", (Object)model.getId(), (Object)e);
        }
        this.log.info("Finished upgrade of workflow model: {}", (Object)model.getId());
    }

    private WorkflowNode traverseRoute(WorkflowTransition transition, Stack<Node> flowStack, Session session) throws RepositoryException {
        WorkflowNode modelNode = transition.getTo();
        this.log.info("Starting traversal of workflow model graph");
        while (!this.isRouteEnd(modelNode)) {
            if (this.isWorkflowStep(modelNode)) {
                Node parent;
                Node step;
                if (modelNode.getType().equals("PARTICIPANT")) {
                    this.log.info("Upgrading participant workflow node");
                    parent = flowStack.peek();
                    step = JcrUtil.createUniqueNode((Node)parent, (String)"participant", (String)"nt:unstructured", (Session)session);
                    step.setProperty("sling:resourceType", "cq/workflow/components/model/participant");
                    this.copyMetaData(modelNode, step);
                } else if (modelNode.getType().equals("PROCESS")) {
                    this.log.info("Upgrading process workflow node");
                    parent = flowStack.peek();
                    step = JcrUtil.createUniqueNode((Node)parent, (String)"process", (String)"nt:unstructured", (Session)session);
                    step.setProperty("sling:resourceType", "cq/workflow/components/model/process");
                    this.copyMetaData(modelNode, step);
                } else if (modelNode.getType().equals("DYNAMIC_PARTICIPANT")) {
                    this.log.info("Upgrading dynamic participant workflow node");
                    parent = flowStack.peek();
                    step = JcrUtil.createUniqueNode((Node)parent, (String)"dynamic_participant", (String)"nt:unstructured", (Session)session);
                    step.setProperty("sling:resourceType", "cq/workflow/components/model/dynamic_participant");
                    this.copyMetaData(modelNode, step);
                } else if (modelNode.getType().equals("CONTAINER")) {
                    this.log.info("Upgrading container workflow node");
                    parent = flowStack.peek();
                    step = JcrUtil.createUniqueNode((Node)parent, (String)"container", (String)"nt:unstructured", (Session)session);
                    step.setProperty("sling:resourceType", "cq/workflow/components/model/container");
                    this.copyMetaData(modelNode, step);
                }
                modelNode = ((WorkflowTransition)modelNode.getTransitions().get(0)).getTo();
                continue;
            }
            if (!this.isWorkflowSplit(modelNode)) continue;
            this.log.info("Upgrading workflow split");
            boolean or = modelNode.getType().equals("OR_SPLIT");
            Node parent = flowStack.peek();
            Node split = JcrUtil.createUniqueNode((Node)parent, (String)(or ? "or" : "and"), (String)"nt:unstructured", (Session)session);
            List transitions = modelNode.getTransitions();
            split.setProperty("sling:resourceType", or ? "cq/workflow/components/model/or" : "cq/workflow/components/model/and");
            split.setProperty("branches", (long)transitions.size());
            if (or) {
                split.setProperty("orSplit", true);
            }
            WorkflowNode routeEnd = null;
            for (int i = 0; i < transitions.size(); ++i) {
                String isDefault;
                String rule = ((WorkflowTransition)transitions.get(i)).getRule();
                if (or && rule != null) {
                    if (this.plainScriptRegex.matcher(rule).matches()) {
                        split.setProperty("script" + (i + 1), rule);
                    } else {
                        split.setProperty("scriptPath" + (i + 1), rule);
                    }
                }
                if ((isDefault = (String)((WorkflowTransition)transitions.get(i)).getMetaDataMap().get("isDefault", String.class)) != null && "true".equals(isDefault)) {
                    split.setProperty("default" + (i + 1), "true");
                }
                Node branch = split.addNode(String.valueOf(i + 1));
                branch.setProperty("sling:resourceType", "cq/flow/components/parsys");
                flowStack.push(branch);
                routeEnd = this.traverseRoute((WorkflowTransition)transitions.get(i), flowStack, session);
            }
            modelNode = ((WorkflowTransition)routeEnd.getTransitions().get(0)).getTo();
        }
        this.log.info("Finished traversal of workflow model graph");
        return modelNode;
    }

    private boolean isWorkflowStep(WorkflowNode node) {
        String type = node.getType();
        return !type.equals("START") && !type.equals("END") && !this.isWorkflowSplit(node);
    }

    private boolean isWorkflowSplit(WorkflowNode node) {
        String type = node.getType();
        return type.equals("OR_SPLIT") || type.equals("AND_SPLIT");
    }

    private boolean isRouteEnd(WorkflowNode node) {
        String type = node.getType();
        return type.equals("END") || type.equals("OR_JOIN") || type.equals("AND_JOIN");
    }

    private void copyMetaData(WorkflowNode source, Node target) throws RepositoryException {
        target.setProperty("jcr:title", source.getTitle());
        target.setProperty("jcr:description", source.getDescription());
        if (!source.getMetaDataMap().isEmpty()) {
            MetaDataMap sourceMetaData = source.getMetaDataMap();
            Node metaData = target.addNode("metaData");
            for (String key : sourceMetaData.keySet()) {
                if ("PARTICIPANT".equals(key)) {
                    String authorizableId = (String)sourceMetaData.get(key, String.class);
                    if (null != authorizableId && authorizableId.length() > 0) {
                        metaData.setProperty(key, this.userManager.get(authorizableId).getHomePath());
                        continue;
                    }
                    this.log.warn("could not set empty authorizable ID for workflow [{}]", (Object)source.getId());
                    continue;
                }
                metaData.setProperty(key, (String)sourceMetaData.get(key, String.class));
            }
        }
    }

    private void adjustLauncherConfig(String originalPath, String upgradedPath, Session session) throws RepositoryException {
        String query = "/jcr:root/etc/workflow/launcher/config//element(*,cq:WorkflowLauncher)[@workflow='" + originalPath + "']";
        NodeIterator iter = session.getWorkspace().getQueryManager().createQuery(query, "xpath").execute().getNodes();
        while (iter.hasNext()) {
            iter.nextNode().setProperty("workflow", upgradedPath);
        }
        session.save();
    }

    private void adjustInstances(String originalPath, String upgradedPath, Session session) throws RepositoryException {
        String query = "SELECT * FROM cq:Workflow WHERE jcr:path LIKE '/etc/workflow/instances/%' AND modelId LIKE '" + originalPath + "'";
        NodeIterator iter = session.getWorkspace().getQueryManager().createQuery(query, "sql").execute().getNodes();
        while (iter.hasNext()) {
            iter.nextNode().setProperty("modelId", upgradedPath);
        }
        session.save();
    }

    private class WorkflowReferenceSearch
    extends TraversingItemVisitor.Default {
        private static final String REFERENCE_PROPERTY_NAME = "CONTAINER";
        private Node modelNode;

        private WorkflowReferenceSearch() {
        }

        protected void entering(Property property, int level) throws RepositoryException {
            if (property.getName().equals("CONTAINER")) {
                String value = property.getString();
                if (Workflow54ModelUpgrade.this.upgradedModelIdMapping.containsKey(value)) {
                    if (this.modelNode != null && !this.modelNode.isCheckedOut()) {
                        this.modelNode.getSession().getWorkspace().getVersionManager().checkout(this.modelNode.getPath());
                    }
                    property.setValue((String)Workflow54ModelUpgrade.this.upgradedModelIdMapping.get(value));
                    property.getSession().save();
                }
            }
        }

        protected void entering(Node node, int level) throws RepositoryException {
            if (node.isNodeType("cq:WorkflowModel")) {
                this.modelNode = node;
            }
        }

        protected void leaving(Node node, int level) throws RepositoryException {
            if (this.modelNode == node && this.modelNode.isCheckedOut()) {
                this.modelNode.getSession().getWorkspace().getVersionManager().checkin(this.modelNode.getPath());
                this.modelNode = null;
            }
        }
    }

}