org.yawlfoundation.yawl.resourcing.ResourceMap.java Source code

Java tutorial

Introduction

Here is the source code for org.yawlfoundation.yawl.resourcing.ResourceMap.java

Source

/*
 * Copyright (c) 2004-2012 The YAWL Foundation. All rights reserved.
 * The YAWL Foundation is a collaboration of individuals and
 * organisations who are committed to improving workflow technology.
 *
 * This file is part of YAWL. YAWL 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.
 *
 * YAWL is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without 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 YAWL. If not, see <http://www.gnu.org/licenses/>.
 */

package org.yawlfoundation.yawl.resourcing;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.yawlfoundation.yawl.engine.YSpecificationID;
import org.yawlfoundation.yawl.engine.interfce.WorkItemRecord;
import org.yawlfoundation.yawl.resourcing.allocators.ShortestQueue;
import org.yawlfoundation.yawl.resourcing.datastore.persistence.Persister;
import org.yawlfoundation.yawl.resourcing.interactions.*;
import org.yawlfoundation.yawl.resourcing.resource.Participant;
import org.yawlfoundation.yawl.resourcing.resource.SecondaryResources;
import org.yawlfoundation.yawl.resourcing.util.TaggedStringList;

import java.util.*;

/**
 * Manages all of the resourcing requirements and distribution strategies for one
 * task.
 *
 *  @author Michael Adams
 *  v0.1, 03/08/2007
 */

public class ResourceMap {

    private ResourceManager rm = ResourceManager.getInstance();

    // interaction points
    private OfferInteraction _offer;
    private AllocateInteraction _allocate;
    private StartInteraction _start;

    // user-task privileges
    private TaskPrivileges _privileges;

    private SecondaryResources _secondary;

    private long _id; // hibernate pkey
    private String _taskID;
    private YSpecificationID _specID;

    private Participant _piledResource = null;
    private String _piledResourceID; // for persistence
    private Persister _persister;

    private Set<TaggedStringList> _ignoreSet = new HashSet<TaggedStringList>();

    // workitem id - offered-to-participants mapping
    private Map<String, Set<Participant>> _offered = new HashMap<String, Set<Participant>>();

    private static final Logger _log = LogManager.getLogger(ResourceMap.class);

    public ResourceMap() {
    } // for persistence

    public ResourceMap(String taskID) {
        _taskID = taskID;
        _offer = new OfferInteraction(taskID);
        _allocate = new AllocateInteraction(taskID);
        _start = new StartInteraction(taskID);
        _secondary = new SecondaryResources();

        _allocate.setAllocator(new ShortestQueue()); // default allocator
    }

    public ResourceMap(YSpecificationID specID, String taskID, Element eleSpec) {
        this(taskID);
        _specID = specID;
        _privileges = new TaskPrivileges(specID, taskID);
        parse(eleSpec);
        restorePiledResource();
    }

    public ResourceMap(YSpecificationID specID, String taskID, Element eleSpec, boolean persisting) {
        this(specID, taskID, eleSpec);
        if (persisting)
            setPersisting(true);
        restorePiledResource();
    }

    public void setOfferInteraction(OfferInteraction oi) {
        _offer = oi;
    }

    public void setAllocateInteraction(AllocateInteraction ai) {
        _allocate = ai;
    }

    public void setStartInteraction(StartInteraction si) {
        _start = si;
    }

    public void setSecondaryResources(SecondaryResources sr) {
        _secondary = sr;
    }

    public void setTaskPrivileges(TaskPrivileges tp) {
        _privileges = tp;
    }

    public OfferInteraction getOfferInteraction() {
        return _offer;
    }

    public AllocateInteraction getAllocateInteraction() {
        return _allocate;
    }

    public StartInteraction getStartInteraction() {
        return _start;
    }

    public SecondaryResources getSecondaryResources() {
        return _secondary;
    }

    public TaskPrivileges getTaskPrivileges() {
        return _privileges;
    }

    public Set<Participant> getDistributionSet() {
        return (_offer != null) ? _offer.getDistributionSet() : null;
    }

    public String getTaskID() {
        return _taskID;
    }

    public void setTaskID(String taskID) {
        _taskID = taskID;
    }

    public String getSpecName() {
        return _specID.getUri();
    }

    public YSpecificationID getSpecID() {
        return _specID;
    }

    public void setSpecID(YSpecificationID specID) {
        _specID = specID;
    }

    public String getPiledResourceID() {
        return _piledResourceID;
    }

    public void setPiledResourceID(String id) {
        _piledResourceID = id;
    }

    public Participant getPiledResource() {
        return _piledResource;
    }

    public String setPiledResource(Participant p, WorkItemRecord wir) {
        String result;
        if (!hasPiledResource()) {
            _piledResource = p;
            _piledResourceID = p.getID();
            if (isPersisting())
                _persister.insert(this);
            if (rm.routePiledWorkItem(_piledResource, wir)) {
                result = "Task successfully piled.";
            } else {
                result = "Cannot pile task: problem starting workitem.";
            }
        } else
            result = "Cannot pile task: already piled by another resource.";

        return result;
    }

    public void removePiledResource() {
        _piledResource = null;
        _piledResourceID = null;
        if (isPersisting()) {

            // have to get persisted map first, so we can delete it (since 'this' is not
            // the same object as the one persisted)
            ResourceMap map = getPersistedMap();
            if (map != null)
                _persister.delete(map);
            _persister.commit();
        }
    }

    private ResourceMap getPersistedMap() {
        ResourceMap result = null;
        if (isPersisting()) {
            String where = String.format(
                    "_specID.identifier='%s' and _specID.version.version='%s' and _taskID='%s'",
                    _specID.getIdentifier(), _specID.getVersionAsString(), _taskID);
            List map = _persister.selectWhere("ResourceMap", where);
            if ((map != null) && (!map.isEmpty())) {
                result = (ResourceMap) map.iterator().next();
            }
        }
        return result;
    }

    public boolean hasPiledResource() {
        return _piledResource != null;
    }

    private void restorePiledResource() {
        ResourceMap map = getPersistedMap();
        if (map != null) {
            if (rm.isPersistPiling()) {
                _piledResourceID = map.getPiledResourceID();
                _piledResource = rm.getOrgDataSet().getParticipant(_piledResourceID);
            } else
                _persister.delete(map);
        }
    }

    public void setPersisting(boolean persist) {
        if ((persist) && (_persister == null))
            _persister = Persister.getInstance();
        else
            _persister = null;
    }

    public boolean isPersisting() {
        return (_persister != null);
    }

    public void ignore(WorkItemRecord wir, Participant p) {
        TaggedStringList ignoredForWorkItem = getIgnoredList(wir.getID());
        if (ignoredForWorkItem != null)
            ignoredForWorkItem.add(p.getID());
        else
            _ignoreSet.add(new TaggedStringList(wir.getID(), p.getID()));
    }

    public TaggedStringList getIgnoredList(String key) {
        TaggedStringList result = null;
        for (TaggedStringList list : _ignoreSet) {
            if (list.getTag().equals(key)) {
                result = list;
                break;
            }
        }
        return result;
    }

    public Set<Participant> getOfferedParticipants(String itemID) {
        return _offered.get(itemID);
    }

    public boolean equals(Object other) {
        if (other instanceof ResourceMap) {
            ResourceMap otherMap = (ResourceMap) other;
            return getSpecID().equals(otherMap.getSpecID()) && getTaskID().equals(otherMap.getTaskID());
        }
        return false;
    }

    public int hashCode() {
        return getSpecID().hashCode() + getTaskID().hashCode();
    }

    /****************************************************************************/
    /****************************************************************************/

    public WorkItemRecord distribute(WorkItemRecord wir) {
        boolean routed = false;

        // if this task is piled, send directly to the piled participant's started queue
        if (_piledResource != null)
            routed = rm.routePiledWorkItem(_piledResource, wir);

        if (!routed) {

            // construct distribution set from resource spec
            Set<Participant> distributionSet = doOffer(wir);
            if (distributionSet != null) {

                // if case is chained and the chained participant is in the
                // distribution set, route it directly to their started queue
                routed = rm.routeIfChained(wir, distributionSet);

                if (!routed) {

                    // not piled or chained, distribute in normal manner
                    removeIgnoredParticipants(wir, distributionSet);
                    removeUnavailableParticipants(distributionSet);
                    if (!distributionSet.isEmpty()) {
                        Participant chosen = doAllocate(distributionSet, wir);
                        if (chosen != null)
                            doStart(chosen, wir);
                    } else {

                        // ignored p's --> empty distribution set: put in unoffered
                        addToAdminUnofferedQueue(wir);
                    }
                }
            }
        }
        return wir;
    }

    /** removes the workitem from all offer queues */
    public void withdrawOffer(WorkItemRecord wir) {
        Set<Participant> pSet = _offered.remove(wir.getID());
        _offer.withdrawOffer(wir, pSet);
    }

    private void addToAdminUnofferedQueue(WorkItemRecord wir) {
        rm.getWorkItemCache().updateResourceStatus(wir, WorkItemRecord.statusResourceUnoffered);
        ResourceAdministrator.getInstance().addToUnoffered(wir);
    }

    private Set<Participant> doOffer(WorkItemRecord wir) {
        Set<Participant> offerSet = null;
        if (_offer.getInitiator() == AbstractInteraction.USER_INITIATED) {

            // put workitem in admin's unoffered queue & DONE
            addToAdminUnofferedQueue(wir);
        } else {
            offerSet = _offer.performOffer(wir);
            if (offerSet.isEmpty()) {
                _log.warn("Parse of resource specifications for workitem {} resulted in"
                        + " an empty distribution set. The workitem will be passed to"
                        + " an administrator for manual distribution.", wir.getID());

                // put workitem in admin's unoffered queue & DONE
                addToAdminUnofferedQueue(wir);
                offerSet = null;
            }
        }
        return offerSet;
    }

    private Participant doAllocate(Set<Participant> pSet, WorkItemRecord wir) {
        Participant chosenOne = null;
        rm.getWorkItemCache().updateResourceStatus(wir, WorkItemRecord.statusResourceOffered);
        if (_allocate.getInitiator() == AbstractInteraction.USER_INITIATED) {

            // for each participant in set, place workitem on their offered queue
            for (Participant p : pSet) {
                QueueSet qs = p.getWorkQueues();
                if (qs == null)
                    qs = p.createQueueSet(rm.isPersisting());
                qs.addToQueue(wir, WorkQueue.OFFERED);
                rm.announceModifiedQueue(p.getID());
            }
            _offered.put(wir.getID(), pSet);
        } else {
            if (rm.isDeferredChoiceHandled(wir)) {
                _log.info("Workitem {} has been withdrawn by the engine.", wir.getID());
                return null;
            }

            chosenOne = _allocate.performAllocation(pSet, wir);
            if (chosenOne != null) {
                rm.setDeferredChoiceHandled(wir);
            } else {
                _log.warn(
                        "The system allocator '{}' has been unable to allocate "
                                + "workitem '{}' to a participant. The workitem has been passed "
                                + "to the administrator's unoffered queue for manual allocation.",
                        _allocate.getAllocator().getName(), wir.getID());
                addToAdminUnofferedQueue(wir);
            }
        }
        return chosenOne;
    }

    private void doStart(Participant p, WorkItemRecord wir) {
        boolean started = false;
        QueueSet qs = p.getWorkQueues();
        if (qs == null)
            qs = p.createQueueSet(rm.isPersisting());

        if (_start.getInitiator() == AbstractInteraction.SYSTEM_INITIATED) {
            started = rm.startImmediate(p, wir);
            if (!started) {
                _log.warn("The workitem '{}' could not be automatically started. The "
                        + "workitem has been placed on the participant's allocated queue.", wir.getID());
            }
        }

        // either initiator is 'user' or start was unsuccessful
        if (!started) {
            qs.addToQueue(wir, WorkQueue.ALLOCATED);
            rm.getWorkItemCache().updateResourceStatus(wir, WorkItemRecord.statusResourceAllocated);
        }
    }

    private void removeIgnoredParticipants(WorkItemRecord wir, Set<Participant> distributionSet) {
        TaggedStringList ignoredForWorkItem = getIgnoredList(wir.getID());
        if (ignoredForWorkItem != null) {
            Set<Participant> ignored = new HashSet<Participant>();
            for (Participant p : distributionSet) {
                if (ignoredForWorkItem.contains(p.getID()))
                    ignored.add(p);
            }
            distributionSet.removeAll(ignored);
        }
    }

    private void removeUnavailableParticipants(Set<Participant> distributionSet) {
        Set<Participant> unavailable = new HashSet<Participant>();
        for (Participant p : distributionSet) {
            if (!p.isAvailable())
                unavailable.add(p);
        }
        distributionSet.removeAll(unavailable);
    }

    public void removeIgnoreList(WorkItemRecord wir) {
        TaggedStringList ignoredForWorkItem = getIgnoredList(wir.getID());
        if (ignoredForWorkItem != null) {
            _ignoreSet.remove(ignoredForWorkItem);
        }
    }

    public void addToOfferedSet(WorkItemRecord wir, Participant p) {
        Set<Participant> pSet = _offered.get(wir.getID());
        if (pSet == null)
            pSet = new HashSet<Participant>();
        pSet.add(p);
        _offered.put(wir.getID(), pSet);
    }

    /**
     * Parse the Element passed for task resourcing info and build the appropriate
     * objects.
     * @param eleSpec the [resourcing] section from a particular task definition
     * within a specification file.
     */
    public void parse(Element eleSpec) {
        if (eleSpec != null) {
            Namespace nsYawl = eleSpec.getNamespace();
            try {
                _offer.parse(eleSpec.getChild("offer", nsYawl), nsYawl);
                _allocate.parse(eleSpec.getChild("allocate", nsYawl), nsYawl);
                _start.parse(eleSpec.getChild("start", nsYawl), nsYawl);
                _secondary.parse(eleSpec.getChild("secondary", nsYawl), nsYawl);
                _privileges.parse(eleSpec.getChild("privileges", nsYawl), nsYawl);
                _log.info("Resourcing specification parse completed for task: {}", _taskID);
            } catch (ResourceParseException rpe) {
                _log.error("Error parsing resourcing specification for task: " + _taskID, rpe);
            }
        }
    }

    public String toXML() {
        StringBuilder xml = new StringBuilder("<resourcing>");
        xml.append(_offer.toXML());
        xml.append(_allocate.toXML());
        xml.append(_start.toXML());
        xml.append(_secondary.toXML());
        xml.append(_privileges.toXML());
        xml.append("</resourcing>");
        return xml.toString();
    }

    // hibernate pkey getter & setter

    public long get_id() {
        return _id;
    }

    public void set_id(long id) {
        _id = id;
    }
}