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

org.cristalise.kernel.entity.agent.Job Maven / Gradle / Ivy

/**
 * This file is part of the CRISTAL-iSE kernel.
 * Copyright (c) 2001-2015 The CRISTAL Consortium. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * http://www.fsf.org/licensing/licenses/lgpl.html
 */
package org.cristalise.kernel.entity.agent;

import static org.cristalise.kernel.graph.model.BuiltInVertexProperties.AGENT_NAME;
import static org.cristalise.kernel.graph.model.BuiltInVertexProperties.DELEGATE_NAME;
import static org.cristalise.kernel.graph.model.BuiltInVertexProperties.OUTCOME_INIT;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.cristalise.kernel.common.GTimeStamp;
import org.cristalise.kernel.common.InvalidDataException;
import org.cristalise.kernel.common.ObjectNotFoundException;
import org.cristalise.kernel.common.PersistencyException;
import org.cristalise.kernel.entity.C2KLocalObject;
import org.cristalise.kernel.entity.proxy.ItemProxy;
import org.cristalise.kernel.graph.model.BuiltInVertexProperties;
import org.cristalise.kernel.lifecycle.instance.Activity;
import org.cristalise.kernel.lifecycle.instance.stateMachine.StateMachine;
import org.cristalise.kernel.lifecycle.instance.stateMachine.Transition;
import org.cristalise.kernel.lookup.AgentPath;
import org.cristalise.kernel.lookup.InvalidAgentPathException;
import org.cristalise.kernel.lookup.InvalidItemPathException;
import org.cristalise.kernel.lookup.ItemPath;
import org.cristalise.kernel.persistency.ClusterType;
import org.cristalise.kernel.persistency.outcome.Outcome;
import org.cristalise.kernel.persistency.outcome.OutcomeInitiator;
import org.cristalise.kernel.persistency.outcome.Schema;
import org.cristalise.kernel.process.Gateway;
import org.cristalise.kernel.querying.Query;
import org.cristalise.kernel.scripting.ErrorInfo;
import org.cristalise.kernel.scripting.Script;
import org.cristalise.kernel.utils.CastorHashMap;
import org.cristalise.kernel.utils.DateUtility;
import org.cristalise.kernel.utils.KeyValuePair;
import org.cristalise.kernel.utils.LocalObjectLoader;
import org.cristalise.kernel.utils.Logger;

import lombok.Getter;
import lombok.Setter;

/**
 * 
 */
@Getter @Setter
public class Job implements C2KLocalObject {
    // Persistent fields
    private int            id;
    private ItemPath       itemPath;
    private String         stepName;
    private String         stepPath;
    private String         stepType;
    private Transition     transition;
    private String         originStateName;
    private String         targetStateName;
    private String         agentRole;
    private AgentPath      agentPath;
    private AgentPath      delegatePath;
    private CastorHashMap  actProps = new CastorHashMap();
    private GTimeStamp     creationDate;

    // Non-persistent fields
    private ErrorInfo  error;
    private ItemProxy  item = null;
    private boolean    transitionResolved = false;

    private Outcome outcome = null;

    /**
     * OutcomeInitiator cache
     */
    static private HashMap ocInitCache = new HashMap();

    /**
     * Empty constructor required for Castor
     */
    public Job() {
        setCreationDate(DateUtility.getNow());
        setActProps(new CastorHashMap());
    }

    /**
     * Main constructor to create Job during workflow enactment
     */
    public Job(Activity act, ItemPath itemPath, Transition transition, AgentPath agent, AgentPath delegate, String role)
            throws InvalidDataException, ObjectNotFoundException, InvalidAgentPathException
    {
        setCreationDate(DateUtility.getNow());
        setItemPath(itemPath);
        setStepPath(act.getPath());
        setTransition(transition);
        setOriginStateName(act.getStateMachine().getState(transition.getOriginStateId()).getName());
        setTargetStateName(act.getStateMachine().getState(transition.getTargetStateId()).getName());
        setStepName(act.getName());
        setStepType(act.getType());
        setAgentPath(agent);
        setAgentRole(role);

        setActPropsAndEvaluateValues(act);

        getItem();
    }

    /**
     * Constructor for recreating Job from backend
     */
    public Job(int id, ItemPath itemPath, String stepName, String stepPath, String stepType, 
            Transition transition, String originStateName, String targetStateName, 
            String agentRole, AgentPath agentPath, AgentPath delegatePath, CastorHashMap actProps, GTimeStamp creationDate)
    {
        super();
        setId(id);
        setItemPath(itemPath);
        setStepName(stepName);
        setStepPath(stepPath);
        setStepType(stepType);
        setTransition(transition);
        setOriginStateName(originStateName);
        setTargetStateName(targetStateName);
        setAgentRole(agentRole);
        setAgentPath(agentPath);
        setDelegatePath(delegatePath);
        setActProps(actProps);
        setCreationDate(creationDate);
    }

    public void setItemPath(ItemPath path) {
        itemPath = path;
        item = null;
    }

    public void setItemUUID( String uuid ) throws InvalidItemPathException {
        setItemPath(new ItemPath(uuid));
    }

    public String getItemUUID() {
        return getItemPath().getUUID().toString();
    }

    public ItemProxy getItem() throws InvalidDataException {
        try {
            return getItemProxy();
        }
        catch (InvalidItemPathException | ObjectNotFoundException e) {
            throw new InvalidDataException(e.getMessage());
        }
    }

    public Transition getTransition() {
        if (transition != null && transitionResolved == false) {
            Logger.msg(8, "Job.getTransition() - actProps:"+actProps);
            try {
                StateMachine sm = LocalObjectLoader.getStateMachine(actProps);
                transition = sm.getTransition(transition.getId());
                transitionResolved = true;
            }
            catch (Exception e) {
                Logger.error(e);
                return transition;
            }
        }
        return transition;
    }

    public void setTransition(Transition transition) {
        this.transition = transition;
        transitionResolved = false;
    }

    /**
     * Used by castor to unmarshall from XML
     * 
     * @param uuid the string representation of UUID
     * @throws InvalidItemPathException Cannot set UUID of agent and delegate from parameter
     */
    public void setAgentUUID( String uuid ) throws InvalidItemPathException {
        if (StringUtils.isBlank(uuid)) { 
            agentPath = null; 
            delegatePath = null;
        }
        else if (uuid.contains(":")) {
            String[] agentStr = uuid.split(":");

            if (agentStr.length!=2) throw new InvalidItemPathException("Cannot set UUID of agent and delegate from string:"+uuid);

            setAgentPath(    new AgentPath(agentStr[0]) );
            setDelegatePath( new AgentPath(agentStr[1]) );
        }
        else
            setAgentPath(new AgentPath(uuid));
    }

    /**
    * Used by castor to marshall to XML
     * @return The stringified UUID of Agent concatenated with ':' and UUID of Delegate if exists
     */
    public String getAgentUUID() {
        if (agentPath != null) {
            if (delegatePath != null) return getAgentPath().getUUID().toString()+":"+getDelegatePath().getUUID().toString();
            else                      return getAgentPath().getUUID().toString();
        }
        return null;
    }

    public String getAgentName() {
        String agentName = null;

        if (agentPath != null) agentName = agentPath.getAgentName();
        if (agentName == null) agentName = (String) actProps.getBuiltInProperty(AGENT_NAME);

        return agentName;
    }

    public String getDelegateName() {
        String delegateName = null;

        if (delegatePath != null) delegateName = delegatePath.getAgentName();
        if (delegateName == null) delegateName = (String) actProps.getBuiltInProperty(DELEGATE_NAME);

        return delegateName;
    }

    public Schema getSchema() throws InvalidDataException, ObjectNotFoundException {
        if (getTransition().hasOutcome(actProps)) {
            return getTransition().getSchema(actProps);
        }
        return null;
    }

    @Deprecated
    public String getSchemaName() throws InvalidDataException, ObjectNotFoundException {
        try {
            return getSchema().getName();
        }
        catch (Exception e) {
            return null;
        }
    }

    @Deprecated
    public int getSchemaVersion() throws InvalidDataException, ObjectNotFoundException {
        try {
            return getSchema().getVersion();
        }
        catch (Exception e) {
            return -1;
        }
    }
    public boolean isOutcomeRequired() {
        return getTransition().hasOutcome(actProps) && getTransition().getOutcome().isRequired();
    }

    public Script getScript() throws ObjectNotFoundException, InvalidDataException {
        if (getTransition().hasScript(actProps)) {
            return getTransition().getScript(actProps);
        }
        return null;
    }

    public Query getQuery() throws ObjectNotFoundException, InvalidDataException {
        if (hasQuery()) {
            Query query = getTransition().getQuery(actProps);
            query.setParemeterValues(itemPath.getUUID().toString(), getSchemaName(), actProps);
            return query;
        }
        return null;
    }

    @Deprecated
    public String getScriptName() {
        try {
            return getScript().getName();
        }
        catch (Exception e) {
            return null;
        }
    }

    @Deprecated
    public int getScriptVersion() throws InvalidDataException {
        try {
            return getScript().getVersion();
        }
        catch (Exception e) {
            return -1;
        }
    }

    public KeyValuePair[] getKeyValuePairs() {
        return actProps.getKeyValuePairs();
    }

    public void setKeyValuePairs(KeyValuePair[] pairs) {
        actProps.setKeyValuePairs(pairs);
    }

    @Override
    public String getName() {
        return Integer.toString(id);
    }

    @Override
    public void setName(String name) {
        id = Integer.parseInt(name);
    }

    public ItemProxy getItemProxy() throws ObjectNotFoundException, InvalidItemPathException {
        if (item == null) item = Gateway.getProxyManager().getProxy(itemPath);
        return item;
    }

    public String getDescription() {
        String desc = (String) actProps.get("Description");
        if (desc == null) desc = "No Description";
        return desc;
    }

    public void setOutcome(String outcomeData) throws InvalidDataException, ObjectNotFoundException {
        setOutcome(new Outcome(-1, outcomeData, transition.getSchema(actProps)));
    }

    public void setOutcome(Outcome o) {
        outcome = o;
    }

    public void setError(ErrorInfo errors) {
        error = errors;
        try {
            setOutcome(Gateway.getMarshaller().marshall(error));
        }
        catch (Exception e) {
            Logger.error("Error marshalling ErrorInfo in job");
            Logger.error(e);
        } 
    }

    /**
     * Checks the value of the 'Viewpoint' ActivityProperty and return 'last' if value is blank 
     * or starts with 'xpath:'. In all other cases it returns its value.
     * 
     * @return the 'calculated' Viewpoint name
     */
    public String getValidViewpointName() {
        String viewName = getActPropString("Viewpoint");

        Logger.msg(5, "Job.getValidViewpointName() - Activity properties Viewpoint:'"+viewName+"'");

        if(StringUtils.isBlank(viewName) || viewName.startsWith("xpath:")) {
            viewName = "last";
        }

        Logger.msg(5, "Job.getValidViewpointName() - returning Viewpoint:'"+viewName+"'");

        return viewName;
    }

    /**
     * Returns the Outcome instance associated with the 'last' Viewpoint
     * 
     * @return Outcome instance
     * @throws InvalidDataException inconsistent data or persistency issue
     * @throws ObjectNotFoundException Schema or Outcome was not found
     */
    public Outcome getLastOutcome() throws InvalidDataException, ObjectNotFoundException {
        try {
            return item.getViewpoint(getSchema().getName(), getValidViewpointName()).getOutcome();
        }
        catch (PersistencyException e) {
            Logger.error(e);
            throw new InvalidDataException("Error loading viewpoint:"+e.getMessage()); 
        }
    }

    /**
     * Returns the Outcome string associated with the 'last' Viewpoint
     * 
     * @return XML data of the last version of Outcome
     * @throws InvalidDataException inconsistent data or persistency issue
     * @throws ObjectNotFoundException Schema or Outcome was not found
     */
    public String getLastView() throws InvalidDataException, ObjectNotFoundException {
        return getLastOutcome().getData();
    }

    /**
     * Retrieve the OutcomeInitiator associated with this Job.
     * 
     * @see BuiltInVertexProperties#OUTCOME_INIT
     * 
     * @return the configured OutcomeInitiator
     * @throws InvalidDataException OutcomeInitiator could not be created
     */
    public OutcomeInitiator getOutcomeInitiator() throws InvalidDataException {
        String ocInitName = getActPropString(OUTCOME_INIT);

        if (StringUtils.isNotBlank(ocInitName)) {
            String ocConfigPropName = OUTCOME_INIT.getName()+"."+ocInitName;
            OutcomeInitiator ocInit;

            synchronized (ocInitCache) {
                Logger.msg(5, "Job.getOutcomeInitiator() - ocConfigPropName:"+ocConfigPropName);
                ocInit = ocInitCache.get(ocConfigPropName);

                if (ocInit == null) {
                    if (!Gateway.getProperties().containsKey(ocConfigPropName)) {
                        throw new InvalidDataException("Property OutcomeInstantiator "+ocConfigPropName+" isn't defined. Check module.xml");
                    }

                    try {
                        ocInit = (OutcomeInitiator)Gateway.getProperties().getInstance(ocConfigPropName);
                        ocInitCache.put(ocConfigPropName, ocInit);
                    }
                    catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
                        Logger.error(e);
                        throw new InvalidDataException("OutcomeInstantiator "+ocConfigPropName+" couldn't be instantiated:"+e.getMessage());
                    }
                }
            }
            return ocInit;
        }
        else
            return null;
    }

    /**
     * Returns the Outcome string. It is based on {@link Job#getOutcome()}
     * 
     * @return the Outcome xml or null
     * @throws InvalidDataException inconsistent data
     */
    public String getOutcomeString() throws InvalidDataException, ObjectNotFoundException {
        if(outcome != null) { 
            return outcome.getData();
        }
        else {
            getOutcome();

            //getOutcome() could return a null object
            if(outcome != null) return outcome.getData();
        }
        return null;
    }

    /**
     * Returns the Outcome if exists otherwise tries to read and duplicate the Outcome of 'last' ViewPoint. 
     * If that does not exists it tries to use an OutcomeInitiator.
     * 
     * @return the Outcome object or null
     * @throws InvalidDataException inconsistent data
     * @throws ObjectNotFoundException Schema was not found
     */
    public Outcome getOutcome() throws InvalidDataException, ObjectNotFoundException {
        if (outcome == null && transition.hasOutcome(actProps)) {
            if( getItem().checkViewpoint(getSchema().getName(), getValidViewpointName()) ) {
                Outcome tempOutcome = getLastOutcome();
                outcome = new Outcome(tempOutcome.getData(), tempOutcome.getSchema());
            }
            else {
                OutcomeInitiator ocInit = getOutcomeInitiator();
                if (ocInit != null) outcome = ocInit.initOutcomeInstance(this);
            }
        }
        else {
            Logger.msg(8, "Job.getOutcome() - Job does not require Outcome job:"+this);
        }
        return outcome;
    }

    public boolean hasOutcome() {
        return transition.hasOutcome(actProps);
    }

    public boolean hasScript() {
        return transition.hasScript(actProps);
    }

    public boolean hasQuery() {
        return transition.hasQuery(actProps);
    }

    public boolean isOutcomeSet() {
        return outcome != null;
    }

    @Override
    public ClusterType getClusterType() {
        return ClusterType.JOB;
    }

    @Override
    public String getClusterPath() {
        return getClusterType()+"/"+id;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((itemPath == null)   ? 0 : itemPath.hashCode());
        result = prime * result + ((stepPath == null)   ? 0 : stepPath.hashCode());
        result = prime * result + ((transition == null) ? 0 : transition.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object other) {
        if (this == other)                  return true;
        if (other == null)                  return false;
        if (getClass() != other.getClass()) return false;

        Job otherJob = (Job) other;

        if (itemPath == null) if (otherJob.itemPath != null) return false;
        else if (!itemPath.equals(otherJob.itemPath))        return false;

        if (stepPath == null) if (otherJob.stepPath != null) return false;
        else if (!stepPath.equals(otherJob.stepPath))        return false;

        if (transition == null) if (otherJob.transition != null) return false;
        else if (!transition.equals(otherJob.transition))        return false;

        return true;
    }

    /**
     * 
     * @param act
     * @throws InvalidDataException 
     */
    private void setActPropsAndEvaluateValues(Activity act) throws InvalidDataException {
        setActProps(act.getProperties());
        
        List errors = new ArrayList();

        for(Map.Entry entry: act.getProperties().entrySet()) {
            try {
                Object newVal = act.evaluatePropertyValue(null, entry.getValue(), null);
                if(newVal != null) actProps.put(entry.getKey(), newVal);
            }
            catch (InvalidDataException | PersistencyException | ObjectNotFoundException e) {
                Logger.error(e);
                errors.add(e.getMessage());
            }
        }

        if(errors.size() != 0) {
            StringBuffer buffer = new StringBuffer();
            for(String msg: errors) buffer.append(msg);
            throw new InvalidDataException(buffer.toString());
        }
    }

    private void setActProps(CastorHashMap actProps) {
        this.actProps = actProps;
    }

    public Object getActProp(String name) {
        return actProps.get(name);
    }

    public Object getActProp(String name, Object defaultValue) {
        Object value = getActProp(name);
        return (value == null) ? defaultValue : value;
    }

    public Object getActProp(BuiltInVertexProperties name) {
        return getActProp(name.getName());
    }

    public Object getActProp(BuiltInVertexProperties name, Object defaultValue) {
        return getActProp(name.getName(), defaultValue);
    }

    public String getActPropString(String name) {
        Object obj = getActProp(name);
        return obj == null ? null : String.valueOf(obj);
    }

    public void setActProp(BuiltInVertexProperties prop, Object value) {
        actProps.setBuiltInProperty(prop, value);
    }

    public void setActProp(String name, Object value) {
        actProps.put(name, value);
    }

    public String getActPropString(BuiltInVertexProperties name) {
        return getActPropString(name.getName());
    }

    /**
     * Searches Activity property names using {@link String#startsWith(String)} method
     * 
     * @param pattern the pattern to be matched
     * @return Map of property name and value
     */
    public Map matchActPropNames(String pattern) {
        Map result = new HashMap();

        for(String propName : actProps.keySet()) {
            if(propName.startsWith(pattern)) result.put(propName, actProps.get(propName));
            //if(propName.matches(pattern)) result.put(propName, actProps.get(propName));
        }

        if(result.size() == 0) {
            Logger.msg(5, "Job.matchActPropNames() - NO properties were found for propName.startsWith(pattern:'"+pattern+"')");
            actProps.dump(8);
        }

        return result;
    }

    @Override
    public String toString() {
        return "[item:"+itemPath+" step:"+stepName+" trans:"+getTransition()+" role:"+agentRole+"]";
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy