au.edu.uts.eng.remotelabs.schedserver.queuer.impl.Queue.java Source code

Java tutorial

Introduction

Here is the source code for au.edu.uts.eng.remotelabs.schedserver.queuer.impl.Queue.java

Source

/**
 * SAHARA Scheduling Server
 *
 * Schedules and assigns local laboratory rigs.
 *
 * @license See LICENSE in the top level directory for complete license terms.
 *
 * Copyright (c) 2010, University of Technology, Sydney
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the distribution.
 *  * Neither the name of the University of Technology, Sydney nor the names 
 *    of its contributors may be used to endorse or promote products derived from 
 *    this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author Michael Diponio (mdiponio)
 * @date 1st April 2010
 */
package au.edu.uts.eng.remotelabs.schedserver.queuer.impl;

import static au.edu.uts.eng.remotelabs.schedserver.dataaccess.entities.ResourcePermission.CAPS_PERMISSION;
import static au.edu.uts.eng.remotelabs.schedserver.dataaccess.entities.ResourcePermission.RIG_PERMISSION;
import static au.edu.uts.eng.remotelabs.schedserver.dataaccess.entities.ResourcePermission.TYPE_PERMISSION;

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

import org.hibernate.Criteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

import au.edu.uts.eng.remotelabs.schedserver.bookings.BookingEngineService;
import au.edu.uts.eng.remotelabs.schedserver.dataaccess.dao.RequestCapabilitiesDao;
import au.edu.uts.eng.remotelabs.schedserver.dataaccess.dao.RigDao;
import au.edu.uts.eng.remotelabs.schedserver.dataaccess.dao.RigTypeDao;
import au.edu.uts.eng.remotelabs.schedserver.dataaccess.entities.MatchingCapabilities;
import au.edu.uts.eng.remotelabs.schedserver.dataaccess.entities.RequestCapabilities;
import au.edu.uts.eng.remotelabs.schedserver.dataaccess.entities.Rig;
import au.edu.uts.eng.remotelabs.schedserver.dataaccess.entities.RigCapabilities;
import au.edu.uts.eng.remotelabs.schedserver.dataaccess.entities.RigType;
import au.edu.uts.eng.remotelabs.schedserver.dataaccess.entities.Session;
import au.edu.uts.eng.remotelabs.schedserver.logger.Logger;
import au.edu.uts.eng.remotelabs.schedserver.logger.LoggerActivator;
import au.edu.uts.eng.remotelabs.schedserver.queuer.QueueActivator;
import au.edu.uts.eng.remotelabs.schedserver.rigoperations.RigAllocator;

/**
 * The queue.
 */
public class Queue {
    /** Singleton instance. */
    private static Queue queue = new Queue();

    /** The rig queues. */
    private Map<Long, InnerQueue> rigQueues;

    /** The rig type queues. */
    private Map<Long, InnerQueue> typeQueues;

    /** The request capabilities queues. */
    private Map<Long, InnerQueue> capabilityQueues;

    /** Comparator to compare which session should be assigned first. */
    private QueueSessionComparator comparator;

    /** Logger. */
    private Logger logger;

    /** Flag to specify if this is a test run. */
    private boolean notTest = true;

    private Queue() {
        this.rigQueues = new HashMap<Long, InnerQueue>();
        this.typeQueues = new HashMap<Long, InnerQueue>();
        this.capabilityQueues = new HashMap<Long, InnerQueue>();

        this.comparator = new QueueSessionComparator();

        this.logger = LoggerActivator.getLogger();
    }

    /**
     * Returns true if the rig, the rig's type or a matching request capabilites is queued.
     * That is, if a session is queued that a rig may be assigned to, <code>true</code>
     * is returned.
     * 
     * @param id identifier of rig
     * @return true if the rig is queued for
     */
    public synchronized boolean isRigQueued(Long id, org.hibernate.Session db) {
        Rig rig = new RigDao(db).get(id);

        if (this.rigQueues.containsKey(rig.getId()) && this.rigQueues.get(rig.getId()).size() > 0) {
            /* The rig itself is queued. */
            return true;
        }

        if (this.typeQueues.containsKey(rig.getRigType().getId())
                && this.typeQueues.get(rig.getRigType().getId()).size() > 0) {
            /* The rig's type is queued. */
            return true;
        }

        for (MatchingCapabilities match : rig.getRigCapabilities().getMatchingCapabilitieses()) {
            RequestCapabilities caps = match.getRequestCapabilities();
            if (this.capabilityQueues.containsKey(caps.getId())
                    && this.capabilityQueues.get(caps.getId()).size() > 0) {
                /* A matching request capability is queued. */
                return true;
            }
        }

        return false;
    }

    /**
     * Attempts to assigned the specified rig to a queued session. If there is
     * queued session for the rig, a queued session for its rig type or a
     * queued session for a request capabilities matching its rig capabilities,
     * the highest precedence request given by the {@link QueueSessionComparator} 
     * is assigned.
     * 
     * @param id identifier of rig
     */
    public synchronized void runRigAssignment(Long id, org.hibernate.Session db) {
        Rig rig = new RigDao(db).get(id);
        if (rig == null || !rig.isOnline() || rig.isInSession())
            return;

        Session targetSes = null;
        String queueType = null;
        Long innerQueueId = null;

        /**********************************************************************
         ** 1) Check if there is a queued session for this specific rig.     **
         **********************************************************************/
        if (this.rigQueues.containsKey(rig.getId()) && this.rigQueues.get(rig.getId()).size() > 0) {
            /* The rig itself is queued, so the rig's queued session is the
             * first target to assign to the rig. */
            innerQueueId = rig.getId();
            targetSes = this.rigQueues.get(innerQueueId).peekHead();
            queueType = RIG_PERMISSION;
        }

        /**********************************************************************
         ** 2) Check if there is a queued session for the rig's rig type.    **
         **********************************************************************/
        if (this.typeQueues.containsKey(rig.getRigType().getId())
                && this.typeQueues.get(rig.getRigType().getId()).size() > 0) {
            Session typeSes = this.typeQueues.get(rig.getRigType().getId()).peekHead();

            if (targetSes == null || this.comparator.compare(targetSes, typeSes) > 0) {
                innerQueueId = rig.getRigType().getId();
                targetSes = typeSes;
                queueType = TYPE_PERMISSION;
            }
        }

        /**********************************************************************
         ** 3) Check each of the rig's rig capabilities matching request     **
         **    capabilities.                                                 **
         **********************************************************************/
        for (MatchingCapabilities match : rig.getRigCapabilities().getMatchingCapabilitieses()) {
            RequestCapabilities caps = match.getRequestCapabilities();
            if (this.capabilityQueues.containsKey(caps.getId())
                    && this.capabilityQueues.get(caps.getId()).size() > 0) {
                Session capsSes = this.capabilityQueues.get(caps.getId()).peekHead();

                if (targetSes == null || this.comparator.compare(targetSes, capsSes) > 0) {
                    innerQueueId = caps.getId();
                    targetSes = capsSes;
                    queueType = CAPS_PERMISSION;
                }
            }
        }

        if (targetSes != null) {
            /* Assign the rig. */
            targetSes = (Session) db.merge(targetSes);

            /******************************************************************
             ** 4) Check the rig isn't booked.                               **
             ******************************************************************/
            BookingEngineService bookings = QueueActivator.getBookingService();
            if (bookings != null && !bookings.putQueuedSession(rig, targetSes, db)) {
                /* The rig is booked so we can't assign it to the queued session. */
                return;
            }

            /******************************************************************
             ** 5) Rig is free and we have a session so assign it.           **
             ******************************************************************/
            /* Remove them from the inner queue and if the inner queue is then
             * empty, remove the inner queue itself. */
            if (RIG_PERMISSION.equals(queueType)) {
                this.rigQueues.get(innerQueueId).getHead();
                if (this.rigQueues.get(innerQueueId).size() == 0) {
                    this.rigQueues.remove(innerQueueId);
                }
            } else if (TYPE_PERMISSION.equals(queueType)) {
                this.typeQueues.get(innerQueueId).getHead();
                if (this.typeQueues.get(innerQueueId).size() == 0) {
                    this.typeQueues.remove(innerQueueId);
                }
            } else if (CAPS_PERMISSION.equals(queueType)) {
                this.capabilityQueues.get(innerQueueId).getHead();
                if (this.capabilityQueues.get(innerQueueId).size() == 0) {
                    this.capabilityQueues.remove(innerQueueId);
                }
            }

            targetSes.setAssignmentTime(new Date());
            targetSes.setAssignedRigName(rig.getName());
            targetSes.setActivityLastUpdated(new Date());
            targetSes.setRig(rig);
            rig.setInSession(true);
            rig.setSession(targetSes);
            db.beginTransaction();
            db.flush();
            db.getTransaction().commit();

            this.logger.info("Assigned " + targetSes.getUserNamespace() + ':' + targetSes.getUserName() + " to rig "
                    + rig.getName() + " (session=" + targetSes.getId() + ").");

            /******************************************************************
             ** 5) Allocate the user to the rig.                             **
             ******************************************************************/
            if (this.notTest) {
                new RigAllocator().allocate(targetSes, db);
            }
        }
    }

    /**
     * Runs rig type assigning by loading free rigs in the type and calling
     * rig assignment.
     *
     * @param id rig type id
     * @param db database session
     */
    public synchronized void runTypeAssignment(Long id, org.hibernate.Session db) {
        RigType type = new RigTypeDao(db).get(id);
        if (type == null)
            return;

        /* Create a query to find a free rig in the rig type. */
        Criteria query = db.createCriteria(Rig.class);
        query.add(Restrictions.eq("rigType", type)).add(Restrictions.eq("active", true))
                .add(Restrictions.eq("online", true)).add(Restrictions.eq("inSession", false))
                .addOrder(Order.asc("lastUpdateTimestamp"));

        @SuppressWarnings("unchecked")
        List<Rig> freeRigs = query.list();
        for (Rig freeRig : freeRigs) {
            if (!this.typeQueues.containsKey(id) || this.typeQueues.get(id).size() == 0) {
                return;
            }
            this.runRigAssignment(freeRig.getId(), db);
        }
    }

    /**
     * Runs request capability assignment by searching for a free matching rig
     * and calling rig assignment.
     * 
     * @param id request capabilities assignmetn
     * @param db database session
     */
    public synchronized void runRequestCapabilitiesAssignment(Long id, org.hibernate.Session db) {
        RequestCapabilities caps = new RequestCapabilitiesDao(db).get(id);
        for (MatchingCapabilities match : caps.getMatchingCapabilitieses()) {
            RigCapabilities rigCaps = match.getRigCapabilities();
            for (Rig rig : rigCaps.getRigs()) {
                if (rig.isActive() && rig.isOnline() && !rig.isInSession()) {
                    /* When a free rig is found, run rig assignment, which should assign
                     * the rig. */
                    this.runRigAssignment(rig.getId(), db);

                    /* If the capability has been assigned, no more work to do. */
                    if (!this.capabilityQueues.containsKey(id) || this.capabilityQueues.get(id).size() == 0) {
                        return;
                    }
                }
            }
        }
    }

    /**
     * Adds a session to the queue and attempts to assign it to a rig.
     * 
     * @param ses session to add
     * @param db database session the session is attached to
     * @return return session 
     */
    public synchronized Session addEntry(Session ses, org.hibernate.Session db) {
        String resourceType = ses.getResourceType();
        Long rID = ses.getRequestedResourceId();
        String requestedName = ses.getRequestedResourceName();

        if (RIG_PERMISSION.equals(resourceType)) {
            this.logger.debug("Adding a session (id=" + ses.getId() + ") to the queue for a rig with id=" + rID
                    + " and name " + requestedName + ".");

            if (!this.rigQueues.containsKey(rID)) {
                this.rigQueues.put(rID, new InnerQueue(resourceType, rID, requestedName));
            }
            this.rigQueues.get(rID).add(ses);
            this.runRigAssignment(rID, db);
        } else if (TYPE_PERMISSION.equals(ses.getResourceType())) {
            this.logger.debug("Adding a session (id=" + ses.getId() + ") to the queue for a rig type with id=" + rID
                    + " and name " + requestedName + ".");

            if (!this.typeQueues.containsKey(rID)) {
                this.typeQueues.put(rID, new InnerQueue(resourceType, rID, requestedName));
            }
            this.typeQueues.get(rID).add(ses);
            this.runTypeAssignment(rID, db);
        } else if (CAPS_PERMISSION.equals(ses.getResourceType())) {
            this.logger.debug("Adding a session (id=" + ses.getId() + ") to the queue for a request capabibilites "
                    + " with id " + rID + " and capabilities " + requestedName + ".");

            if (!this.capabilityQueues.containsKey(rID)) {
                this.capabilityQueues.put(rID, new InnerQueue(resourceType, rID, requestedName));
            }
            this.capabilityQueues.get(rID).add(ses);
            this.runRequestCapabilitiesAssignment(rID, db);
        }

        return ses;
    }

    /**
     * Removes the session from the queue.
     * 
     * @param ses session to remove
     * @param db database session
     */
    public synchronized void removeEntry(Session ses, org.hibernate.Session db) {
        long rID = ses.getRequestedResourceId();
        if (ses.getRig() == null) {
            /******************************************************************
             ** Session is in queue, so remove it from its requested resource *
             ** inner queue.                                                  *
             ******************************************************************/
            if (RIG_PERMISSION.equals(ses.getResourceType())
                    && (this.rigQueues.containsKey(rID) && this.rigQueues.get(rID).contains(ses))) {
                this.rigQueues.get(rID).remove(ses);
                if (this.rigQueues.get(rID).size() == 0) {
                    this.rigQueues.remove(rID);
                }
            } else if (TYPE_PERMISSION.equals(ses.getResourceType())
                    && (this.typeQueues.containsKey(rID) && this.typeQueues.get(rID).contains(ses))) {
                this.typeQueues.get(rID).remove(ses);
                if (this.typeQueues.get(rID).size() == 0) {
                    this.typeQueues.remove(rID);
                }
            } else if (CAPS_PERMISSION.equals(ses.getResourceType())
                    && (this.capabilityQueues.containsKey(rID) && this.capabilityQueues.get(rID).contains(ses))) {
                this.capabilityQueues.get(rID).remove(ses);
                if (this.capabilityQueues.get(rID).size() == 0) {
                    this.capabilityQueues.remove(rID);
                }
            }
        }
    }

    /**
     * Gets the position in the queue for the specified session.
     * 
     * @param ses session to find position of
     * @return queue position
     */
    public synchronized int getEntryPosition(Session ses, org.hibernate.Session db) {
        String resourceType = ses.getResourceType();
        Long rID = ses.getRequestedResourceId();
        int pos = 0;

        if (RIG_PERMISSION.equals(resourceType)) {
            /* Position in the rig queue. */
            if (this.rigQueues.containsKey(rID)) {
                pos += this.rigQueues.get(rID).position(ses);
            }

            /* Position in the type queue for the rig. */
            Rig rig = new RigDao(db).get(rID);
            if (this.typeQueues.containsKey(rig.getRigType().getId())) {
                pos += this.typeQueues.get(rig.getRigType().getId()).numberBefore(ses);
            }

            /* Position in the request capabilities queues. */
            for (MatchingCapabilities match : rig.getRigCapabilities().getMatchingCapabilitieses()) {
                RequestCapabilities caps = match.getRequestCapabilities();
                if (this.capabilityQueues.containsKey(caps.getId())) {
                    pos += this.capabilityQueues.get(caps.getId()).numberBefore(ses);
                }
            }
        } else if (TYPE_PERMISSION.equals(resourceType)) {
            /* Position in the type queue. */
            if (this.typeQueues.containsKey(rID)) {
                pos += this.typeQueues.get(rID).position(ses);
            }

            /* Position in the rig queues for the types rigs. */
            RigType rigType = new RigTypeDao(db).get(rID);
            List<Long> checkedCaps = new ArrayList<Long>();

            for (Rig rig : rigType.getRigs()) {
                if (this.rigQueues.containsKey(rig.getId())) {
                    pos += this.rigQueues.get(rig.getId()).numberBefore(ses);
                }

                /* Check unique rig capabilities. */
                Long capsID = rig.getRigCapabilities().getId();
                if (!checkedCaps.contains(capsID)) {
                    checkedCaps.add(capsID);
                    /* Position in the request capabilities queues. */
                    for (MatchingCapabilities match : rig.getRigCapabilities().getMatchingCapabilitieses()) {
                        RequestCapabilities caps = match.getRequestCapabilities();
                        if (this.capabilityQueues.containsKey(caps.getId())) {
                            pos += this.capabilityQueues.get(caps.getId()).numberBefore(ses);
                        }
                    }
                }
            }
        } else if (CAPS_PERMISSION.equals(resourceType)) {
            List<Long> checkedCaps = new ArrayList<Long>();
            List<Long> checkedTypes = new ArrayList<Long>();

            /* The capability queue. */
            if (this.capabilityQueues.containsKey(rID)) {
                checkedCaps.add(rID);
                pos += this.capabilityQueues.get(rID).position(ses);
            }

            RequestCapabilities reqCaps = new RequestCapabilitiesDao(db).get(rID);
            for (MatchingCapabilities match : reqCaps.getMatchingCapabilitieses()) {
                RigCapabilities rigCaps = match.getRigCapabilities();
                for (Rig r : rigCaps.getRigs()) {
                    /* The rig's queue. */
                    if (this.rigQueues.containsKey(r.getId())) {
                        pos += this.rigQueues.get(r.getId()).numberBefore(ses);
                    }

                    /* The rig type's queue */
                    Long typeID = r.getRigType().getId();
                    if (!checkedTypes.contains(typeID)) {
                        checkedTypes.contains(typeID);
                        if (this.typeQueues.containsKey(typeID)) {
                            pos += this.typeQueues.get(typeID).numberBefore(ses);
                        }
                    }

                    /* The rig's rig capabilities queues. */
                    for (MatchingCapabilities m : r.getRigCapabilities().getMatchingCapabilitieses()) {
                        Long capsID = m.getRequestCapabilities().getId();
                        if (!checkedCaps.contains(capsID)) {
                            checkedCaps.add(capsID);
                            if (this.capabilityQueues.containsKey(capsID)) {
                                pos += this.capabilityQueues.get(capsID).numberBefore(ses);
                            }
                        }
                    }
                }
            }
        }

        return pos;
    }

    /**
     * Clears the queue of all sessions.
     */
    public void expunge() {
        this.rigQueues.clear();
        this.typeQueues.clear();
        this.capabilityQueues.clear();
    }

    /**
     * Returns the singleton instance of this queue.
     * 
     * @return queue instance.
     */
    public static Queue getInstance() {
        return Queue.queue;
    }
}